openapi: 3.1.0
info:
  title: API do Financeiro Inteligente
  version: 1.0.0
  description: |-
    Você que é dev, divirta-se! ✨

    > **Índice da documentação** — acesse o índice completo em [https://docs.finance.kobana.com.br/llms.txt](https://docs.finance.kobana.com.br/llms.txt). Use este arquivo para descobrir todas as páginas disponíveis antes de explorar.

    ## Autenticação

    Toda requisição precisa de **duas** coisas: o token, que diz qual **organização** você é, e o cabeçalho `X-Workspace-Id`, que diz em qual **espaço de trabalho** dela agir.

    ```
    Authorization: Bearer <token>
    X-Workspace-Id: <uuid do espaço de trabalho>
    ```

    ### O token

    Bearer JWT assinado em **RS256** pelo servidor de autorização da Kobana (HS512 apenas contra um servidor de desenvolvimento local).

    | Claim | Papel |
    | :--- | :--- |
    | **`org`** | **O tenant.** O `public_id` da organização — é o que decide quais dados o token alcança. |
    | `sub` | A **pessoa** que autorizou. Existe para auditoria e **não** delimita nada. |
    | `scope` | Lista separada por espaços (ex.: `finance.payables finance.people.write`). Um escopo `.write` também vale como leitura. |
    | `jti` | Identificador do token, usado para revogação. |
    | `iss` / `aud` / `exp` | Emissor, destinatário e expiração. |

    ⚠️ O tenant é `org`, nunca `sub`. São espaços de identificadores diferentes: um id de pessoa jamais corresponde a uma organização, então um token montado com o valor errado falha em toda requisição.

    ### O cabeçalho `X-Workspace-Id`

    Uma organização pode ter **vários espaços de trabalho**, e toda rota desta API responde por exatamente **um**. O token sozinho não diz qual — por isso o cabeçalho é obrigatório em todas as operações.

    Ele é um **seletor, nunca uma permissão**: o espaço informado é conferido contra a organização do token. Um espaço que pertence a outra organização responde exatamente como um que não existe — de propósito, para a resposta não revelar que aquele id é real em outro lugar.

    | Situação | Resposta |
    | :--- | :--- |
    | Cabeçalho ausente | `400` `WORKSPACE_REQUIRED` |
    | Espaço de outra organização, inexistente, ou id malformado | `403` `UNKNOWN_WORKSPACE` |
    | `org` não corresponde a nenhuma organização | `403` `UNKNOWN_ORGANIZATION` |
    | Escopo insuficiente para a operação | `403` `INSUFFICIENT_SCOPE` |
    | Uma regra de Segurança do espaço barrou a operação (`details.reason` diz qual) | `403` `SECURITY_BLOCKED` |

    ### Exemplo

    ```bash
    curl https://api.finance.kobana.com.br/v1/payables \
      -H "Authorization: Bearer $KOBANA_TOKEN" \
      -H "X-Workspace-Id: $KOBANA_WORKSPACE_ID"
    ```

    Para testes em linha de comando, exporte os dois: `export KOBANA_TOKEN=xxxxxxxx` e `export KOBANA_WORKSPACE_ID=xxxxxxxx`, e cole os comandos da documentação no terminal.

    ## Formato

    A API aceita apenas o formato `JSON`. Todas as requisições devem usar `Content-Type: application/json`. Todas as respostas usam `snake_case`.

    | Tipo de Campo | Formato |
    | :--- | :--- |
    | **DateTime** | Formato [ISO8601](https://pt.wikipedia.org/wiki/ISO_8601). Exemplos — Data: `2026-01-24`. Data e Hora: `2026-01-24T10:07:00.000Z` |
    | **Money** | Decimal como string com 2 casas (`"150.00"`). Sempre positivo — direção indicada pelo campo `type` (`debit`/`credit`) ou pelo contexto (payable/receivable). |
    | **UUID** | Identificadores de recursos no formato UUID v4 |

    ## Convenções

    Convenções usadas nesta documentação:

    | Convenção | Descrição |
    | :--- | :--- |
    | **`:id`** | Parâmetro de path que deve ser substituído pelo UUID do recurso. |
    | **`#{variable}`** | Valor da sua conta que deve ser substituído no exemplo. |
    | **`...`** | Conteúdo da resposta truncado para facilitar a leitura. |
    | **`$KOBANA_TOKEN`** | Token JWT de acesso. Exporte com `export KOBANA_TOKEN=xxxxxxxx`. |
    | **`$KOBANA_WORKSPACE_ID`** | UUID do espaço de trabalho, enviado em `X-Workspace-Id`. Exporte com `export KOBANA_WORKSPACE_ID=xxxxxxxx`. |

    ## Ambientes

    | Ambiente | Base URL |
    | :--- | :--- |
    | **Produção** | `https://api.finance.kobana.com.br/v1` |
    | **Sandbox** | `https://api.finance.sandbox.kobana.com.br/v1` |

    ## Códigos de Retorno

    A API retorna códigos HTTP padrão:

    | | Código | Descrição |
    | :--- | :--- | :--- |
    | ✅ | **200 OK** | Requisição bem-sucedida com corpo de resposta. |
    | ✅ | **201 Created** | Recurso criado com sucesso. |
    | ✅ | **204 No Content** | Requisição bem-sucedida, sem corpo de resposta. |
    | ❌ | **400 Bad Request** | Requisição inválida — conteúdo mal formado, filtro não permitido, ou `X-Workspace-Id` ausente. |
    | ❌ | **401 Unauthorized** | Token ausente, inválido ou expirado. |
    | ❌ | **403 Forbidden** | Escopo insuficiente, organização desconhecida, ou espaço de trabalho fora dela. |
    | ❌ | **404 Not Found** | Recurso não encontrado ou fora do workspace. |
    | ❌ | **422 Unprocessable Entity** | Requisição válida, mas os dados enviados não são. |
    | ❌ | **429 Too Many Requests** | Limite de requisições atingido. |
    | ❌ | **500 Internal Server Error** | Erro interno no processamento. Consulte o [status dos servidores](https://status.kobana.com.br). |

    ## Listagem e Paginação

    Todos os endpoints de listagem usam **paginação por cursor**:

    ```
    GET /v1/payables?cursor=<opaque_cursor>&limit=25
    ```

    A resposta inclui `meta.next_cursor` — passe-o como `?cursor=` na próxima requisição para buscar a página seguinte. Quando `meta.next_cursor` é `null`, não há mais páginas.

    ## Idempotência

    Operações de criação (`POST`) aceitam o cabeçalho `X-Idempotency-Key`. Reenvios com a mesma chave retornam o resultado original sem criar duplicatas — seguro para retry em caso de falha de rede.

    ```
    X-Idempotency-Key: <uuid-v4-gerado-pelo-cliente>
    ```

    ## ID das Requisições (Request ID)

    Cada requisição possui um identificador associado, disponível no cabeçalho `Request-Id` da resposta. Esse valor ajuda na depuração e auditoria. O log de requisições fica disponível por **30 dias**. Ao abrir um chamado de suporte sobre uma requisição específica, informe o `Request-Id` para acelerar a investigação.

    ## Segurança

    A API usa certificados **SSL 2048 bits**. Toda requisição deve ser feita via **HTTPS** — chamadas na porta 80 são redirecionadas para 443.

    Os clientes devem suportar `TLSv1.2` ou `TLSv1.3` com uma das cifras: `TLS_AES_128_GCM_SHA256`, `TLS_AES_256_GCM_SHA384`, `TLS_CHACHA20_POLY1305_SHA256`, `ECDHE-RSA-AES128-GCM-SHA256`, `ECDHE-RSA-AES128-SHA256`, `ECDHE-RSA-AES256-GCM-SHA384`, `ECDHE-RSA-CHACHA20-POLY1305`, `ECDHE-RSA-AES256-SHA384`. `TLSv1` e `TLSv1.1` não são suportados.

    ## Cache HTTP

    Use os cabeçalhos HTTP de cache para reduzir carga e ganhar velocidade. A maioria das respostas inclui `ETag` e/ou `Last-Modified` — armazene esses valores e reenvie nas próximas requisições via `If-None-Match` e `If-Modified-Since`. Se o recurso não mudou, a resposta será `304 Not Modified`, sem corpo e sem reprocessamento.

    ## Tratamento de Erros

    Erros 5xx indicam falhas no servidor: **500 Internal Server Error** (aplicação indisponível), **502 Bad Gateway**, **503 Service Unavailable** e **504 Gateway Timeout** (falhas pontuais de infraestrutura). Sua aplicação deve identificar esses códigos e reagendar a requisição após alguns minutos com backoff exponencial. Status dos servidores: [https://status.kobana.com.br](https://status.kobana.com.br).

    ## Spec OpenAPI

    Importe a spec no Postman ou Insomnia para ter uma coleção pronta que sempre reflete a API atual:

    - **YAML:** `/api/v1/openapi.yaml`
    - **JSON:** `/api/v1/openapi.json`
  license:
    name: Proprietary — Kobana
    url: https://kobana.com.br
servers:
  - url: https://api.finance.kobana.com.br/v1
    description: Production
  - url: https://api.finance.sandbox.kobana.com.br/v1
    description: Sandbox
components:
  schemas:
    ApiV1Error:
      type: object
      properties:
        error:
          type: object
          properties:
            code:
              type: string
              description: Stable error code from the Kobana vocabulary. Do not parse the message — switch on this.
              example: INVALID_TOKEN
            message:
              type: string
              description: Human-readable, English. Never includes user-supplied data verbatim.
              example: Missing Authorization header.
            details:
              description: Optional, error-specific payload (e.g. `{ required, granted }` on `INSUFFICIENT_SCOPE`).
          required:
            - code
            - message
          description: Error envelope. Always present on 4xx and 5xx responses.
      required:
        - error
      description: "The error envelope every failure answers with: `{ error: { code, message, details? } }`. `code` is a stable UPPER_SNAKE identifier; `details` carries field-level information when there is any."
    TransactionClassificationCenterAllocationV1:
      type: object
      properties:
        classification_center:
          type: object
          properties:
            id:
              type: string
              format: uuid
            name:
              type:
                - string
                - "null"
          required:
            - id
            - name
          description: The classification center. `name` is `null` if the join is unavailable.
        percentage:
          type:
            - number
            - "null"
          description: Percentage of the transaction amount, as a number. `null` when the split is amount-driven.
        amount:
          type:
            - number
            - "null"
          description: Absolute slice in BRL. `null` when the split is percentage-driven.
      required:
        - classification_center
        - percentage
        - amount
      description: "How a financial transaction is split across classification centers: one center and its share."
    TransactionCustomClassificationV1:
      type: object
      properties:
        custom_classifier:
          type: object
          properties:
            id:
              type: string
              format: uuid
            name:
              type:
                - string
                - "null"
          required:
            - id
            - name
          description: The dimension. `name` is `null` if the join is unavailable.
        custom_classifier_value:
          type:
            - object
            - "null"
          properties:
            id:
              type: string
              format: uuid
            name:
              type:
                - string
                - "null"
          required:
            - id
            - name
          description: Assigned list value (id + label). `null` for scalar dimensions (see `raw_value`) or when the join is unavailable.
        raw_value:
          type:
            - string
            - "null"
          description: Free value for scalar dimensions; `null` for list dimensions.
        percentage:
          type:
            - number
            - "null"
          description: Share of the amount as a number. `null` for native single-select assignments.
        amount:
          type:
            - number
            - "null"
          description: Absolute slice in BRL. `null` for native single-select assignments.
      required:
        - custom_classifier
        - custom_classifier_value
        - raw_value
        - percentage
        - amount
      description: A custom classifier value applied to a financial transaction.
    FinancialTransactionV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
          example: 8c8b4c4f-2e0e-4f6e-9b8a-2a1c2b3d4e5f
        company_id:
          type: string
          format: uuid
        financial_account_id:
          type: string
          format: uuid
        description:
          type: string
          description: Short label written by the user — distinct from `bank_description`.
          example: Pagamento de fornecedor
        bank_description:
          type:
            - string
            - "null"
          description: Raw description from the bank statement, when imported.
        document:
          type:
            - string
            - "null"
          description: Document number (NF, boleto, comprovante) that originated this movement.
        document_date:
          type:
            - string
            - "null"
          format: date-time
          description: Issue date of the source document (NF, boleto). Distinct from `occurred_at` (settlement) and `competence_date` (accounting period).
          example: 2026-05-20T00:00:00.000Z
        amount:
          type: number
          description: A number in BRL, 2 decimal places. Always positive — direction is carried by `type`.
          example: 150
        occurred_at:
          type: string
          format: date-time
          description: When the movement hit the account.
          example: 2026-05-24T00:00:00.000Z
        competence_date:
          type:
            - string
            - "null"
          format: date-time
          description: Accounting period this movement belongs to. Falls back to `occurred_at` when null.
        type:
          type: string
          enum:
            - debit
            - credit
          description: "`debit` = money out, `credit` = money in."
        category:
          type:
            - object
            - "null"
          properties:
            id:
              type: string
              format: uuid
          required:
            - id
          additionalProperties: false
        person:
          type:
            - object
            - "null"
          properties:
            id:
              type: string
              format: uuid
          required:
            - id
          additionalProperties: false
        classification_center:
          type:
            - object
            - "null"
          properties:
            id:
              type: string
              format: uuid
          required:
            - id
          additionalProperties: false
          description: Primary classification center (rows[0] of the rateio). Full split in `classification_center_allocations[]`.
        classification_center_allocations:
          type: array
          items:
            $ref: "#/components/schemas/TransactionClassificationCenterAllocationV1"
          description: The rateio (split) across classification centers. Empty when there is no split.
        custom_classifications:
          type: array
          items:
            $ref: "#/components/schemas/TransactionCustomClassificationV1"
          description: Custom-classifier assignments (one per dimension for native single-select). Empty when none.
        payment_method_kind:
          type:
            - string
            - "null"
          enum:
            - bank_billet
            - ted
            - pix
            - credit_card
            - debit_card
            - cash
            - check
            - direct_debit
            - other
            - none
            - null
        classified:
          type: boolean
        notes:
          type:
            - string
            - "null"
        metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadados do sistema, gerenciados pela Kobana. Somente leitura.
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: "Metadados do usuário: objeto JSON livre."
        created_via:
          type:
            - string
            - "null"
          enum:
            - import
            - dashboard
            - api
            - cli
            - mcp
            - other
            - null
          description: "Channel that created the record: `dashboard`, `import`, `api`, `mcp`, `cli` or `other`. On API calls it is the `X-Request-Source` header. `null` when the change came from the system (scheduled jobs) or predates this tracking."
          example: api
        created_via_other:
          type:
            - string
            - "null"
          description: App that created the record when the channel is `other` (`X-Request-Source-Other`).
          example: null
        created_by_email:
          type:
            - string
            - "null"
          description: E-mail of the person who created the record. `null` for system changes.
          example: maria@empresa.com.br
        updated_via:
          type:
            - string
            - "null"
          enum:
            - import
            - dashboard
            - api
            - cli
            - mcp
            - other
            - null
          description: "Channel that last changed the record: `dashboard`, `import`, `api`, `mcp`, `cli` or `other`. On API calls it is the `X-Request-Source` header. `null` when the change came from the system (scheduled jobs) or predates this tracking."
          example: api
        updated_via_other:
          type:
            - string
            - "null"
          description: App that last changed the record when the channel is `other` (`X-Request-Source-Other`).
          example: null
        updated_by_email:
          type:
            - string
            - "null"
          description: E-mail of the person who last changed the record. `null` for system changes.
          example: maria@empresa.com.br
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
          description: Last write. Filterable and sortable — it is the incremental-sync window. Rows predating the column carry their `created_at`, since the real last-write date was never recorded.
      required:
        - id
        - company_id
        - financial_account_id
        - description
        - bank_description
        - document
        - document_date
        - amount
        - occurred_at
        - competence_date
        - type
        - category
        - person
        - classification_center
        - classification_center_allocations
        - custom_classifications
        - payment_method_kind
        - classified
        - notes
        - metadata
        - custom_metadata
        - created_via
        - created_via_other
        - created_by_email
        - updated_via
        - updated_via_other
        - updated_by_email
        - created_at
        - updated_at
      description: "A financial transaction (lançamento): a credit or debit on a financial account, realised or forecast, with its classification."
    FinancialTransactionListMeta:
      type: object
      properties:
        next_cursor:
          type:
            - string
            - "null"
          description: Opaque cursor. Pass back as `?cursor=…` to fetch the next page. `null` when there is no next page.
      required:
        - next_cursor
      description: "Pagination metadata of a financial transactions listing: `next_cursor` to pass as `cursor` for the next page (null on the last)."
    FinancialTransactionListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/FinancialTransactionV1"
        meta:
          $ref: "#/components/schemas/FinancialTransactionListMeta"
      required:
        - data
        - meta
      description: "One page of financial transactions: the records in `data` and the pagination in `meta`."
    TransactionClassificationCenterSplitInput:
      type: object
      properties:
        classification_center:
          type: object
          properties:
            id:
              type: string
              format: uuid
          required:
            - id
          additionalProperties: false
        percentage:
          type:
            - number
            - "null"
        amount:
          type:
            - number
            - "null"
          description: Allocated amount in BRL, in BRL (`"150.00"`). Provide `percentage` and/or `amount`. A JSON number is still accepted for backward compatibility; the string is canonical and is what the reads return.
      required:
        - classification_center
      description: One line of the classification-center split of a financial transaction, as sent on create or update.
    TransactionCustomClassificationInput:
      type: object
      properties:
        custom_classifier:
          type: object
          properties:
            id:
              type: string
              format: uuid
          required:
            - id
          additionalProperties: false
        custom_classifier_value:
          type: object
          properties:
            id:
              type: string
              format: uuid
            name:
              type: string
              minLength: 1
              maxLength: 255
          additionalProperties: false
          description: "Assigned value: `{id}` for an existing value, or `{name}` to find-or-create it under the dimension."
        raw_value:
          type: string
          maxLength: 2000
          description: Free value for a scalar dimension. Use instead of a value id/name.
        percentage:
          type: number
        amount:
          type: number
          description: Optional rateio amount in BRL, in BRL. Omit for native single-select.
      required:
        - custom_classifier
      description: A custom classifier value to apply to a financial transaction, as sent on create or update.
    FinancialTransactionCreateRequest:
      type: object
      properties:
        company_id:
          type: string
          format: uuid
          description: Owning company UUID. Must belong to the tenant and match the account's `company_id` (or the account must be workspace-level).
        financial_account_id:
          type: string
          format: uuid
          description: Settlement account UUID. Must belong to the tenant.
        description:
          type: string
          minLength: 1
          maxLength: 500
        bank_description:
          type:
            - string
            - "null"
          maxLength: 500
        document:
          type:
            - string
            - "null"
          maxLength: 100
        document_date:
          type:
            - string
            - "null"
          format: date-time
          description: Issue date of the source document (ISO 8601). Pass `null` (PATCH) to clear.
        amount:
          type: number
          description: A number in BRL (up to 13 integer digits, 2 decimals). Always positive — direction is carried by `type`.
          example: 1500
        occurred_at:
          type: string
          format: date-time
          description: When the movement hit the account (ISO 8601).
        competence_date:
          type:
            - string
            - "null"
          format: date-time
          description: Accounting period this movement belongs to. Defaults to `occurred_at` when omitted.
        type:
          type: string
          enum:
            - debit
            - credit
          description: "`debit` = money out, `credit` = money in. Drives the polarity for find-or-create on `{name}` fallbacks."
        category:
          type:
            - object
            - "null"
          properties:
            id:
              type: string
              format: uuid
            name:
              type: string
              minLength: 1
              maxLength: 255
          additionalProperties: false
          description: "`{id}` for an existing category, or `{name}` to find-or-create one scoped to (workspace, company) with polarity derived from `type`. Sending both `id` and `name` returns 400."
        person:
          type:
            - object
            - "null"
          properties:
            id:
              type: string
              format: uuid
            name:
              type: string
              minLength: 1
              maxLength: 255
          additionalProperties: false
          description: "`{id}` for an existing person, or `{name}` to find-or-create one with `customer` polarity for credits / `supplier` for debits. Never both."
        classification_center:
          type:
            - object
            - "null"
          properties:
            id:
              type: string
              format: uuid
            name:
              type: string
              minLength: 1
              maxLength: 255
          additionalProperties: false
          description: "`{id}` for an existing classification center, or `{name}` to find-or-create one with `revenue` polarity for credits / `cost` for debits — a center typed `both` matches either; creates with the narrow polarity when no match. Never both."
        classification_centers:
          type: array
          items:
            $ref: "#/components/schemas/TransactionClassificationCenterSplitInput"
          description: "Rateio (split) across classification centers. Replace-all: the array becomes the row's set; an empty array clears it; omit to leave it alone. Effective amounts must sum to `amount` (1-cent tolerance). Each center's polarity must match the transaction `type` (`both` matches either). When present, the single `classification_center` field is ignored and the legacy FK mirrors rows[0]."
        custom_classifications:
          type: array
          items:
            $ref: "#/components/schemas/TransactionCustomClassificationInput"
          description: "Custom-classifier assignments. Replace-all: the array becomes the row's set; an empty array clears it; omit to leave it alone. One value per dimension for native single-select."
        payment_method_kind:
          type:
            - string
            - "null"
          enum:
            - bank_billet
            - ted
            - pix
            - credit_card
            - debit_card
            - cash
            - check
            - direct_debit
            - other
            - none
            - null
        classified:
          type: boolean
          description: Defaults to `false`. Setting `true` marks the row as already-categorised in the UI.
        notes:
          type:
            - string
            - "null"
        is_pending:
          type: boolean
          description: Defaults to `false`. When `true`, the row is treated as a forecast — excluded from realised-balance and dashboard income/expense aggregates.
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: "Metadados do usuário: objeto JSON livre. Envie `null` para limpar. Máx. 64 KB serializado, 16 níveis."
      required:
        - company_id
        - financial_account_id
        - description
        - amount
        - occurred_at
        - type
      additionalProperties: false
      description: Request body to create a financial transaction.
    FinancialTransactionUpdateRequest:
      type: object
      properties:
        description:
          type: string
          minLength: 1
          maxLength: 500
        bank_description:
          type:
            - string
            - "null"
          maxLength: 500
        document:
          type:
            - string
            - "null"
          maxLength: 100
        document_date:
          type:
            - string
            - "null"
          format: date-time
          description: Issue date of the source document (ISO 8601). Pass `null` (PATCH) to clear.
        amount:
          type: number
          description: A number in BRL (up to 13 integer digits, 2 decimals). Always positive — direction is carried by `type`.
          example: 1500
        occurred_at:
          type: string
          format: date-time
          description: When the movement hit the account (ISO 8601).
        competence_date:
          type:
            - string
            - "null"
          format: date-time
          description: Accounting period this movement belongs to. Defaults to `occurred_at` when omitted.
        type:
          type: string
          enum:
            - debit
            - credit
          description: "`debit` = money out, `credit` = money in. Drives the polarity for find-or-create on `{name}` fallbacks."
        category:
          type:
            - object
            - "null"
          properties:
            id:
              type: string
              format: uuid
            name:
              type: string
              minLength: 1
              maxLength: 255
          additionalProperties: false
          description: "`{id}` for an existing category, or `{name}` to find-or-create one scoped to (workspace, company) with polarity derived from `type`. Sending both `id` and `name` returns 400."
        person:
          type:
            - object
            - "null"
          properties:
            id:
              type: string
              format: uuid
            name:
              type: string
              minLength: 1
              maxLength: 255
          additionalProperties: false
          description: "`{id}` for an existing person, or `{name}` to find-or-create one with `customer` polarity for credits / `supplier` for debits. Never both."
        classification_center:
          type:
            - object
            - "null"
          properties:
            id:
              type: string
              format: uuid
            name:
              type: string
              minLength: 1
              maxLength: 255
          additionalProperties: false
          description: "`{id}` for an existing classification center, or `{name}` to find-or-create one with `revenue` polarity for credits / `cost` for debits — a center typed `both` matches either; creates with the narrow polarity when no match. Never both."
        classification_centers:
          type: array
          items:
            $ref: "#/components/schemas/TransactionClassificationCenterSplitInput"
          description: "Rateio (split) across classification centers. Replace-all: the array becomes the row's set; an empty array clears it; omit to leave it alone. Effective amounts must sum to `amount` (1-cent tolerance). Each center's polarity must match the transaction `type` (`both` matches either). When present, the single `classification_center` field is ignored and the legacy FK mirrors rows[0]."
        custom_classifications:
          type: array
          items:
            $ref: "#/components/schemas/TransactionCustomClassificationInput"
          description: "Custom-classifier assignments. Replace-all: the array becomes the row's set; an empty array clears it; omit to leave it alone. One value per dimension for native single-select."
        payment_method_kind:
          type:
            - string
            - "null"
          enum:
            - bank_billet
            - ted
            - pix
            - credit_card
            - debit_card
            - cash
            - check
            - direct_debit
            - other
            - none
            - null
        classified:
          type: boolean
          description: Defaults to `false`. Setting `true` marks the row as already-categorised in the UI.
        notes:
          type:
            - string
            - "null"
        is_pending:
          type: boolean
          description: Defaults to `false`. When `true`, the row is treated as a forecast — excluded from realised-balance and dashboard income/expense aggregates.
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: "Metadados do usuário: objeto JSON livre. Envie `null` para limpar. Máx. 64 KB serializado, 16 níveis."
        custom_metadata_mode:
          type: string
          enum:
            - replace
            - merge
          description: "Como o `custom_metadata` enviado é reconciliado com o valor armazenado: `replace` (padrão) substitui o objeto inteiro, `merge` faz deep merge sobre o valor atual. Aceito apenas na atualização."
      additionalProperties: false
      description: "Request body to update a financial transaction: send only the fields to change."
    TaxV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
          example: b3d9a5f8-2c1e-4a90-9f23-7e6b1c4d8a02
        name:
          type: string
          description: Tax kind label (e.g. IRRF, INSS, ISS). Unique per workspace.
          example: IRRF
        default_rate:
          type:
            - number
            - "null"
          description: Suggested default rate as a percent with up to 2 fractional digits (`1.5` = 1.5%). Form-side convenience — the charge stores its own BRL amount, not derived from this rate.
          example: 1.5
        default_withholding:
          type: boolean
          description: When true, new tax charges default to `withholding=true` (imposto retido). Most withheld BR taxes (IRRF, INSS) lean true.
        active:
          type: boolean
          description: When false, the tax is hidden from new charges (soft-deactivation). Existing charges referencing it are preserved.
        metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadados do sistema, gerenciados pela Kobana. Somente leitura.
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: "Metadados do usuário: objeto JSON livre."
        created_via:
          type:
            - string
            - "null"
          enum:
            - import
            - dashboard
            - api
            - cli
            - mcp
            - other
            - null
          description: "Channel that created the record: `dashboard`, `import`, `api`, `mcp`, `cli` or `other`. On API calls it is the `X-Request-Source` header. `null` when the change came from the system (scheduled jobs) or predates this tracking."
          example: api
        created_via_other:
          type:
            - string
            - "null"
          description: App that created the record when the channel is `other` (`X-Request-Source-Other`).
          example: null
        created_by_email:
          type:
            - string
            - "null"
          description: E-mail of the person who created the record. `null` for system changes.
          example: maria@empresa.com.br
        updated_via:
          type:
            - string
            - "null"
          enum:
            - import
            - dashboard
            - api
            - cli
            - mcp
            - other
            - null
          description: "Channel that last changed the record: `dashboard`, `import`, `api`, `mcp`, `cli` or `other`. On API calls it is the `X-Request-Source` header. `null` when the change came from the system (scheduled jobs) or predates this tracking."
          example: api
        updated_via_other:
          type:
            - string
            - "null"
          description: App that last changed the record when the channel is `other` (`X-Request-Source-Other`).
          example: null
        updated_by_email:
          type:
            - string
            - "null"
          description: E-mail of the person who last changed the record. `null` for system changes.
          example: maria@empresa.com.br
        created_at:
          type: string
          format: date-time
      required:
        - id
        - name
        - default_rate
        - default_withholding
        - active
        - metadata
        - custom_metadata
        - created_via
        - created_via_other
        - created_by_email
        - updated_via
        - updated_via_other
        - updated_by_email
        - created_at
      description: A tax configuration (ISS, IRRF, INSS…) applied to payables and receivables.
    TaxListMeta:
      type: object
      properties:
        next_cursor:
          type:
            - string
            - "null"
          description: Opaque cursor. Pass back as `?cursor=…` to fetch the next page. `null` when there is no next page.
      required:
        - next_cursor
      description: "Pagination metadata of a taxes listing: `next_cursor` to pass as `cursor` for the next page (null on the last)."
    TaxListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/TaxV1"
        meta:
          $ref: "#/components/schemas/TaxListMeta"
      required:
        - data
        - meta
      description: "One page of taxes: the records in `data` and the pagination in `meta`."
    TaxCreateRequest:
      type: object
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 100
          description: Tax label. Unique per workspace.
          example: IRRF
        default_rate:
          type:
            - number
            - "null"
          description: Suggested default rate as a percent (0–100, up to 2 decimals). `null`/omitted = no default.
          example: 1.5
        default_withholding:
          type: boolean
          description: Defaults to `true` (imposto retido).
        active:
          type: boolean
          description: Defaults to `true`.
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: "Metadados do usuário: objeto JSON livre. Envie `null` para limpar. Máx. 64 KB serializado, 16 níveis."
      required:
        - name
      additionalProperties: false
      description: Request body to create a tax.
    TaxUpdateRequest:
      type: object
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 100
          description: Tax label. Unique per workspace.
          example: IRRF
        default_rate:
          type:
            - number
            - "null"
          description: Suggested default rate as a percent (0–100, up to 2 decimals). `null`/omitted = no default.
          example: 1.5
        default_withholding:
          type: boolean
          description: Defaults to `true` (imposto retido).
        active:
          type: boolean
          description: Defaults to `true`.
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: "Metadados do usuário: objeto JSON livre. Envie `null` para limpar. Máx. 64 KB serializado, 16 níveis."
      additionalProperties: false
      description: "Request body to update a tax: send only the fields to change."
    CompanyAddressV1:
      type: object
      properties:
        street:
          type:
            - string
            - "null"
          example: Rua das Notas
        number:
          type:
            - string
            - "null"
          example: "100"
        complement:
          type:
            - string
            - "null"
          example: Sala 4
        neighborhood:
          type:
            - string
            - "null"
          example: Centro
        city:
          type:
            - string
            - "null"
          example: Londrina
        city_ibge_code:
          type:
            - string
            - "null"
          description: IBGE municipality code. Required by the fiscal registries, which never key by name.
          example: "4113700"
        state:
          type:
            - string
            - "null"
          description: Two-letter state (ISO 3166-2).
          example: PR
        zip_code:
          type:
            - string
            - "null"
          description: Digits only.
          example: "86010000"
      required:
        - street
        - number
        - complement
        - neighborhood
        - city
        - city_ibge_code
        - state
        - zip_code
      description: The address of a company.
    CompanyV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
          example: 62057dab-0f98-4020-ae1e-2c0803251b1e
        name:
          type: string
          description: Legal name as registered.
          example: ACME Comércio Ltda
        cnpj:
          type: string
          description: 14-character CNPJ string (no formatting / no dots / no dashes). The first 12 characters are alphanumeric (A-Z uppercase + 0-9); the last 2 are numeric check digits. Numeric-only CNPJs (legacy format) and alphanumeric CNPJs (new RFB format) are both valid.
          example: "12345678000190"
        type:
          type: string
          enum:
            - matriz
            - filial
          description: "`matriz` is the head office; `filial` is a branch. Unique per workspace, scoped by `cnpj`."
        active:
          type: boolean
          description: When false, the company is soft-deactivated. Existing transactions/people/etc. referencing it are preserved.
        trade_name:
          type:
            - string
            - "null"
          description: Nome fantasia. `null` when not informed.
          example: ACME
        municipal_registration:
          type:
            - string
            - "null"
          description: Inscrição Municipal — prerequisite for issuing NFS-e in the municipality.
          example: "123456"
        tax_regime:
          type:
            - string
            - "null"
          enum:
            - SimplesNacional
            - SimplesNacionalExcessoSublimite
            - MicroempreendedorIndividual
            - LucroPresumido
            - LucroReal
            - Isento
            - null
          description: Tax regime, spelled as the Brazilian fiscal wire spells it. `null` when not informed.
          example: LucroPresumido
        address:
          allOf:
            - $ref: "#/components/schemas/CompanyAddressV1"
            - type:
                - object
                - "null"
              description: Fiscal address. `null` when none is on file.
        metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadados do sistema, gerenciados pela Kobana. Somente leitura.
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: "Metadados do usuário: objeto JSON livre."
        created_via:
          type:
            - string
            - "null"
          enum:
            - import
            - dashboard
            - api
            - cli
            - mcp
            - other
            - null
          description: "Channel that created the record: `dashboard`, `import`, `api`, `mcp`, `cli` or `other`. On API calls it is the `X-Request-Source` header. `null` when the change came from the system (scheduled jobs) or predates this tracking."
          example: api
        created_via_other:
          type:
            - string
            - "null"
          description: App that created the record when the channel is `other` (`X-Request-Source-Other`).
          example: null
        created_by_email:
          type:
            - string
            - "null"
          description: E-mail of the person who created the record. `null` for system changes.
          example: maria@empresa.com.br
        updated_via:
          type:
            - string
            - "null"
          enum:
            - import
            - dashboard
            - api
            - cli
            - mcp
            - other
            - null
          description: "Channel that last changed the record: `dashboard`, `import`, `api`, `mcp`, `cli` or `other`. On API calls it is the `X-Request-Source` header. `null` when the change came from the system (scheduled jobs) or predates this tracking."
          example: api
        updated_via_other:
          type:
            - string
            - "null"
          description: App that last changed the record when the channel is `other` (`X-Request-Source-Other`).
          example: null
        updated_by_email:
          type:
            - string
            - "null"
          description: E-mail of the person who last changed the record. `null` for system changes.
          example: maria@empresa.com.br
        created_at:
          type: string
          format: date-time
      required:
        - id
        - name
        - cnpj
        - type
        - active
        - trade_name
        - municipal_registration
        - tax_regime
        - address
        - metadata
        - custom_metadata
        - created_via
        - created_via_other
        - created_by_email
        - updated_via
        - updated_via_other
        - updated_by_email
        - created_at
      description: A company (CNPJ) of the workspace. Every payable, receivable and account belongs to one.
    CompanyListMeta:
      type: object
      properties:
        next_cursor:
          type:
            - string
            - "null"
          description: Opaque cursor. Pass back as `?cursor=…` to fetch the next page. `null` when there is no next page.
      required:
        - next_cursor
      description: "Pagination metadata of a companies listing: `next_cursor` to pass as `cursor` for the next page (null on the last)."
    CompanyListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/CompanyV1"
        meta:
          $ref: "#/components/schemas/CompanyListMeta"
      required:
        - data
        - meta
      description: "One page of companies: the records in `data` and the pagination in `meta`."
    CompanyCreateRequest:
      type: object
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 255
          description: Legal name as registered.
          example: ACME Comércio Ltda
        cnpj:
          type: string
          minLength: 1
          description: CNPJ — 14 characters. Formatting (dots / slash / dash) is accepted and stripped; letters are uppercased. Supports both numeric-only (legacy) and alphanumeric (new RFB format) CNPJs. Validated with the standard BR check-digit algorithm. **Required** — a missing, invalid, or document-less value returns 422. Must be unique among active companies in the workspace (duplicate → 409). `type` (matriz/filial) is derived server-side.
          example: "12345678000190"
        trade_name:
          type: string
          maxLength: 255
        municipal_registration:
          type: string
          maxLength: 30
        tax_regime:
          type: string
          enum:
            - SimplesNacional
            - SimplesNacionalExcessoSublimite
            - MicroempreendedorIndividual
            - LucroPresumido
            - LucroReal
            - Isento
        address:
          type: object
          properties:
            street:
              type: string
              maxLength: 255
            number:
              type: string
              maxLength: 30
            complement:
              type: string
              maxLength: 255
            neighborhood:
              type: string
              maxLength: 255
            city:
              type: string
              maxLength: 255
            city_ibge_code:
              type: string
              maxLength: 10
            state:
              type: string
              minLength: 2
              maxLength: 2
            zip_code:
              type: string
              maxLength: 10
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: "Metadados do usuário: objeto JSON livre. Envie `null` para limpar. Máx. 64 KB serializado, 16 níveis."
      required:
        - name
        - cnpj
      additionalProperties: false
      description: Request body to create a company.
    CompanyUpdateRequest:
      type: object
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 255
        active:
          type: boolean
          description: Set `false` to soft-deactivate the company (existing references are preserved).
        trade_name:
          type: string
          maxLength: 255
        municipal_registration:
          type: string
          maxLength: 30
        tax_regime:
          type: string
          enum:
            - SimplesNacional
            - SimplesNacionalExcessoSublimite
            - MicroempreendedorIndividual
            - LucroPresumido
            - LucroReal
            - Isento
        address:
          type:
            - object
            - "null"
          properties:
            street:
              type: string
              maxLength: 255
            number:
              type: string
              maxLength: 30
            complement:
              type: string
              maxLength: 255
            neighborhood:
              type: string
              maxLength: 255
            city:
              type: string
              maxLength: 255
            city_ibge_code:
              type: string
              maxLength: 10
            state:
              type: string
              minLength: 2
              maxLength: 2
            zip_code:
              type: string
              maxLength: 10
          description: Replaces the fiscal address wholesale. `null` clears it; omitting it leaves it untouched. It is never merged field by field — a partial merge would leave a company registered under half of one address and half of another.
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: "Metadados do usuário: objeto JSON livre. Envie `null` para limpar. Máx. 64 KB serializado, 16 níveis."
        custom_metadata_mode:
          type: string
          enum:
            - replace
            - merge
          description: "Como o `custom_metadata` enviado é reconciliado com o valor armazenado: `replace` (padrão) substitui o objeto inteiro, `merge` faz deep merge sobre o valor atual. Aceito apenas na atualização."
      additionalProperties: false
      description: "Request body to update a company: send only the fields to change."
    ClassificationCenterV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
          example: f8d2a5e1-3c47-4b90-9a1b-2c8f7e3d4b95
        company_id:
          type: string
          format: uuid
          description: Owning company. Classification centers are company-scoped (unlike taxes which are workspace-scoped).
        name:
          type: string
          description: Classification center label. Unique constraint not enforced at DB level.
          example: Operacional
        type:
          type: string
          enum:
            - cost
            - revenue
            - both
          description: "`cost` (deducts from gross result), `revenue` (adds to gross result), or `both` (usable on either side). Determines which operations the center is eligible for: payables accept `cost`/`both`, receivables accept `revenue`/`both`."
        description:
          type:
            - string
            - "null"
          description: Free-text annotation. May be null.
        active:
          type: boolean
          description: When false, the classification center is soft-deactivated. Existing payables/receivables/transactions referencing it are preserved.
        metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadados do sistema, gerenciados pela Kobana. Somente leitura.
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: "Metadados do usuário: objeto JSON livre."
        created_via:
          type:
            - string
            - "null"
          enum:
            - import
            - dashboard
            - api
            - cli
            - mcp
            - other
            - null
          description: "Channel that created the record: `dashboard`, `import`, `api`, `mcp`, `cli` or `other`. On API calls it is the `X-Request-Source` header. `null` when the change came from the system (scheduled jobs) or predates this tracking."
          example: api
        created_via_other:
          type:
            - string
            - "null"
          description: App that created the record when the channel is `other` (`X-Request-Source-Other`).
          example: null
        created_by_email:
          type:
            - string
            - "null"
          description: E-mail of the person who created the record. `null` for system changes.
          example: maria@empresa.com.br
        updated_via:
          type:
            - string
            - "null"
          enum:
            - import
            - dashboard
            - api
            - cli
            - mcp
            - other
            - null
          description: "Channel that last changed the record: `dashboard`, `import`, `api`, `mcp`, `cli` or `other`. On API calls it is the `X-Request-Source` header. `null` when the change came from the system (scheduled jobs) or predates this tracking."
          example: api
        updated_via_other:
          type:
            - string
            - "null"
          description: App that last changed the record when the channel is `other` (`X-Request-Source-Other`).
          example: null
        updated_by_email:
          type:
            - string
            - "null"
          description: E-mail of the person who last changed the record. `null` for system changes.
          example: maria@empresa.com.br
        created_at:
          type: string
          format: date-time
      required:
        - id
        - company_id
        - name
        - type
        - description
        - active
        - metadata
        - custom_metadata
        - created_via
        - created_via_other
        - created_by_email
        - updated_via
        - updated_via_other
        - updated_by_email
        - created_at
      description: A classification center (cost or revenue center) to split payables, receivables and transactions by.
    ClassificationCenterListMeta:
      type: object
      properties:
        next_cursor:
          type:
            - string
            - "null"
          description: Opaque cursor. Pass back as `?cursor=…` to fetch the next page. `null` when there is no next page.
      required:
        - next_cursor
      description: "Pagination metadata of a classification centers listing: `next_cursor` to pass as `cursor` for the next page (null on the last)."
    ClassificationCenterListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/ClassificationCenterV1"
        meta:
          $ref: "#/components/schemas/ClassificationCenterListMeta"
      required:
        - data
        - meta
      description: "One page of classification centers: the records in `data` and the pagination in `meta`."
    ClassificationCenterCreateRequest:
      type: object
      properties:
        company_id:
          type: string
          format: uuid
          description: Owning company UUID. Must belong to the tenant.
        name:
          type: string
          minLength: 1
          maxLength: 255
        type:
          type: string
          enum:
            - cost
            - revenue
            - both
          description: "`cost` (deducts from gross result), `revenue` (adds), or `both` (usable on either side)."
        description:
          type:
            - string
            - "null"
          maxLength: 500
        active:
          type: boolean
          description: Defaults to `true`.
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: "Metadados do usuário: objeto JSON livre. Envie `null` para limpar. Máx. 64 KB serializado, 16 níveis."
      required:
        - company_id
        - name
        - type
      additionalProperties: false
      description: Request body to create a classification center.
    ClassificationCenterUpdateRequest:
      type: object
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 255
        type:
          type: string
          enum:
            - cost
            - revenue
            - both
        description:
          type:
            - string
            - "null"
          maxLength: 500
        active:
          type: boolean
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: "Metadados do usuário: objeto JSON livre. Envie `null` para limpar. Máx. 64 KB serializado, 16 níveis."
        custom_metadata_mode:
          type: string
          enum:
            - replace
            - merge
          description: "Como o `custom_metadata` enviado é reconciliado com o valor armazenado: `replace` (padrão) substitui o objeto inteiro, `merge` faz deep merge sobre o valor atual. Aceito apenas na atualização."
      additionalProperties: false
      description: "Request body to update a classification center: send only the fields to change."
    CategoryV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
          example: f8d2a5e1-3c47-4b90-9a1b-2c8f7e3d4b95
        company_id:
          type: string
          format: uuid
          description: Owning company. Categories are company-scoped (siblings across companies do not share an `id`).
        name:
          type: string
          description: Category label.
          example: Aluguel
        parent_id:
          type:
            - string
            - "null"
          format: uuid
          description: Parent category. `null` for root-level categories. Filter `?level=1` to list roots, or `?parent_id=<uuid>` to list direct children of one parent.
        level:
          type: integer
          description: Pre-computed depth in the tree (root = 1, direct child = 2, …). Denormalised on the row — no recursive lookup needed on the client.
          example: 1
        type:
          type: string
          enum:
            - debit
            - credit
          description: "`debit` (cost — money out) or `credit` (revenue — money in). Matches the transaction sign convention."
        metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadados do sistema, gerenciados pela Kobana. Somente leitura.
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: "Metadados do usuário: objeto JSON livre."
        created_via:
          type:
            - string
            - "null"
          enum:
            - import
            - dashboard
            - api
            - cli
            - mcp
            - other
            - null
          description: "Channel that created the record: `dashboard`, `import`, `api`, `mcp`, `cli` or `other`. On API calls it is the `X-Request-Source` header. `null` when the change came from the system (scheduled jobs) or predates this tracking."
          example: api
        created_via_other:
          type:
            - string
            - "null"
          description: App that created the record when the channel is `other` (`X-Request-Source-Other`).
          example: null
        created_by_email:
          type:
            - string
            - "null"
          description: E-mail of the person who created the record. `null` for system changes.
          example: maria@empresa.com.br
        updated_via:
          type:
            - string
            - "null"
          enum:
            - import
            - dashboard
            - api
            - cli
            - mcp
            - other
            - null
          description: "Channel that last changed the record: `dashboard`, `import`, `api`, `mcp`, `cli` or `other`. On API calls it is the `X-Request-Source` header. `null` when the change came from the system (scheduled jobs) or predates this tracking."
          example: api
        updated_via_other:
          type:
            - string
            - "null"
          description: App that last changed the record when the channel is `other` (`X-Request-Source-Other`).
          example: null
        updated_by_email:
          type:
            - string
            - "null"
          description: E-mail of the person who last changed the record. `null` for system changes.
          example: maria@empresa.com.br
        created_at:
          type: string
          format: date-time
      required:
        - id
        - company_id
        - name
        - parent_id
        - level
        - type
        - metadata
        - custom_metadata
        - created_via
        - created_via_other
        - created_by_email
        - updated_via
        - updated_via_other
        - updated_by_email
        - created_at
      description: A category of the chart of accounts, used to classify payables, receivables and transactions. Categories nest.
    CategoryListMeta:
      type: object
      properties:
        next_cursor:
          type:
            - string
            - "null"
          description: Opaque cursor. Pass back as `?cursor=…` to fetch the next page. `null` when there is no next page.
      required:
        - next_cursor
      description: "Pagination metadata of a categories listing: `next_cursor` to pass as `cursor` for the next page (null on the last)."
    CategoryListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/CategoryV1"
        meta:
          $ref: "#/components/schemas/CategoryListMeta"
      required:
        - data
        - meta
      description: "One page of categories: the records in `data` and the pagination in `meta`."
    CategoryCreateRequest:
      type: object
      properties:
        company_id:
          type: string
          format: uuid
          description: Owning company UUID. Must belong to the tenant.
        name:
          type: string
          minLength: 1
          maxLength: 255
        type:
          type: string
          enum:
            - debit
            - credit
          description: "`debit` (cost) or `credit` (revenue). A subcategory must match its parent's type."
        parent_id:
          type:
            - string
            - "null"
          format: uuid
          description: Parent category UUID. Omit (or `null`) for a root (`level` 1). When set, the server computes `level = parent.level + 1` (max 3) and requires `type` to match the parent.
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: "Metadados do usuário: objeto JSON livre. Envie `null` para limpar. Máx. 64 KB serializado, 16 níveis."
      required:
        - company_id
        - name
        - type
      additionalProperties: false
      description: Request body to create a category.
    CategoryUpdateRequest:
      type: object
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 255
        type:
          type: string
          enum:
            - debit
            - credit
          description: Changing a parent's type cascades to all descendants.
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: "Metadados do usuário: objeto JSON livre. Envie `null` para limpar. Máx. 64 KB serializado, 16 níveis."
        custom_metadata_mode:
          type: string
          enum:
            - replace
            - merge
          description: "Como o `custom_metadata` enviado é reconciliado com o valor armazenado: `replace` (padrão) substitui o objeto inteiro, `merge` faz deep merge sobre o valor atual. Aceito apenas na atualização."
      additionalProperties: false
      description: "Request body to update a category: send only the fields to change."
    PersonV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
          example: 1b0bee9d-3db5-4c64-a204-afc83f54d55e
        company_id:
          type: string
          format: uuid
        name:
          type: string
          description: Display name. Required.
          example: ACME Comércio Ltda
        legal_name:
          type:
            - string
            - "null"
          description: Razão social (juridical persons) — often blank for natural persons.
        nickname:
          type:
            - string
            - "null"
        kind:
          type:
            - string
            - "null"
          enum:
            - natural
            - juridical
            - null
          description: "`natural` (pessoa física) or `juridical` (pessoa jurídica). `null` for legacy rows where the kind was never set."
        document_type:
          type:
            - string
            - "null"
          enum:
            - cpf
            - cnpj
            - null
          description: Document type for `document_number`. `cpf` for natural, `cnpj` for juridical.
        document_number:
          type:
            - string
            - "null"
          description: Numeric-only string (no formatting / no dots / no dashes). 11 digits for CPF, 14 for CNPJ. Unique per (company, document_number) at the DB level.
          example: "12345678901"
        is_customer:
          type: boolean
          description: True when the person can receive a `receivables` charge.
        is_supplier:
          type: boolean
          description: True when the person can receive a `payables` charge.
        is_bank_manager:
          type: boolean
          description: True when the person is a bank manager serving one of the company's financial accounts.
        is_team:
          type: boolean
          description: True when the person is part of the company's own team (partners, finance staff).
        active:
          type: boolean
          description: When false, the person is soft-deactivated (hidden from new forms). Existing payables/receivables/transactions referencing it stay valid.
        emails:
          type: array
          items:
            type: string
          description: Email addresses as a JSON array. Empty when no contact email is known.
        phones:
          type: array
          items:
            type: string
          description: Phone numbers as a JSON array (E.164-ish strings). Empty when no contact phone is known.
        notes:
          type:
            - string
            - "null"
        metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadados do sistema, gerenciados pela Kobana. Somente leitura. Vínculos com integrações ficam em `metadata.<slug>` = `{ id, payload }` — por exemplo `banking_gateway` (gateway bancário Kobana).
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: "Metadados do usuário: objeto JSON livre."
        created_via:
          type:
            - string
            - "null"
          enum:
            - import
            - dashboard
            - api
            - cli
            - mcp
            - other
            - null
          description: "Channel that created the record: `dashboard`, `import`, `api`, `mcp`, `cli` or `other`. On API calls it is the `X-Request-Source` header. `null` when the change came from the system (scheduled jobs) or predates this tracking."
          example: api
        created_via_other:
          type:
            - string
            - "null"
          description: App that created the record when the channel is `other` (`X-Request-Source-Other`).
          example: null
        created_by_email:
          type:
            - string
            - "null"
          description: E-mail of the person who created the record. `null` for system changes.
          example: maria@empresa.com.br
        updated_via:
          type:
            - string
            - "null"
          enum:
            - import
            - dashboard
            - api
            - cli
            - mcp
            - other
            - null
          description: "Channel that last changed the record: `dashboard`, `import`, `api`, `mcp`, `cli` or `other`. On API calls it is the `X-Request-Source` header. `null` when the change came from the system (scheduled jobs) or predates this tracking."
          example: api
        updated_via_other:
          type:
            - string
            - "null"
          description: App that last changed the record when the channel is `other` (`X-Request-Source-Other`).
          example: null
        updated_by_email:
          type:
            - string
            - "null"
          description: E-mail of the person who last changed the record. `null` for system changes.
          example: maria@empresa.com.br
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
        - id
        - company_id
        - name
        - legal_name
        - nickname
        - kind
        - document_type
        - document_number
        - is_customer
        - is_supplier
        - is_bank_manager
        - is_team
        - active
        - emails
        - phones
        - notes
        - metadata
        - custom_metadata
        - created_via
        - created_via_other
        - created_by_email
        - updated_via
        - updated_via_other
        - updated_by_email
        - created_at
        - updated_at
      description: A person — supplier, customer or both — with documents, contacts and payment details.
    PersonListMeta:
      type: object
      properties:
        next_cursor:
          type:
            - string
            - "null"
          description: Opaque cursor. Pass back as `?cursor=…` to fetch the next page. `null` when there is no next page.
      required:
        - next_cursor
      description: "Pagination metadata of a people listing: `next_cursor` to pass as `cursor` for the next page (null on the last)."
    PersonListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/PersonV1"
        meta:
          $ref: "#/components/schemas/PersonListMeta"
      required:
        - data
        - meta
      description: "One page of people: the records in `data` and the pagination in `meta`."
    PersonCreateRequest:
      type: object
      properties:
        company_id:
          type: string
          format: uuid
          description: Owning company UUID. Must belong to the tenant.
        name:
          type: string
          minLength: 1
          maxLength: 255
        legal_name:
          type:
            - string
            - "null"
          maxLength: 255
          description: Razão social (juridical persons).
        nickname:
          type:
            - string
            - "null"
          maxLength: 255
        kind:
          type:
            - string
            - "null"
          enum:
            - natural
            - juridical
            - null
          description: "`natural` (pessoa física) or `juridical` (pessoa jurídica)."
        document_type:
          type:
            - string
            - "null"
          enum:
            - cpf
            - cnpj
            - null
          description: Document type for `document_number`. Inferred from the number length when omitted.
        document_number:
          type:
            - string
            - "null"
          maxLength: 20
          description: CPF (11 digits) or CNPJ (14 digits). Formatting is stripped; check digits are validated.
        is_customer:
          type: boolean
          description: Defaults to `false`. True ⇒ can receive a receivable.
        is_supplier:
          type: boolean
          description: Defaults to `false`. True ⇒ can receive a payable.
        is_bank_manager:
          type: boolean
          description: Defaults to `false`.
        is_team:
          type: boolean
          description: Defaults to `false`.
        active:
          type: boolean
          description: Defaults to `true`.
        notes:
          type:
            - string
            - "null"
          maxLength: 2000
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: "Metadados do usuário: objeto JSON livre. Envie `null` para limpar. Máx. 64 KB serializado, 16 níveis."
      required:
        - company_id
        - name
      additionalProperties: false
      description: Request body to create a person (supplier, customer or both).
    PersonUpdateRequest:
      type: object
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 255
        legal_name:
          type:
            - string
            - "null"
          maxLength: 255
          description: Razão social (juridical persons).
        nickname:
          type:
            - string
            - "null"
          maxLength: 255
        kind:
          type:
            - string
            - "null"
          enum:
            - natural
            - juridical
            - null
          description: "`natural` (pessoa física) or `juridical` (pessoa jurídica)."
        document_type:
          type:
            - string
            - "null"
          enum:
            - cpf
            - cnpj
            - null
          description: Document type for `document_number`. Inferred from the number length when omitted.
        document_number:
          type:
            - string
            - "null"
          maxLength: 20
          description: CPF (11 digits) or CNPJ (14 digits). Formatting is stripped; check digits are validated.
        is_customer:
          type: boolean
          description: Defaults to `false`. True ⇒ can receive a receivable.
        is_supplier:
          type: boolean
          description: Defaults to `false`. True ⇒ can receive a payable.
        is_bank_manager:
          type: boolean
          description: Defaults to `false`.
        is_team:
          type: boolean
          description: Defaults to `false`.
        active:
          type: boolean
          description: Defaults to `true`.
        notes:
          type:
            - string
            - "null"
          maxLength: 2000
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: "Metadados do usuário: objeto JSON livre. Envie `null` para limpar. Máx. 64 KB serializado, 16 níveis."
        custom_metadata_mode:
          type: string
          enum:
            - replace
            - merge
          description: "Como o `custom_metadata` enviado é reconciliado com o valor armazenado: `replace` (padrão) substitui o objeto inteiro, `merge` faz deep merge sobre o valor atual. Aceito apenas na atualização."
      additionalProperties: false
      description: "Request body to update a person (supplier, customer or both): send only the fields to change."
    FinancialAccountV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
          example: fe023ef2-a70f-4a7c-a413-abdfe4977a19
        company_id:
          type:
            - string
            - "null"
          format: uuid
          description: Owning company. `null` for workspace-level accounts (no company link). Filter `?company_id=<uuid>` returns only accounts of that company.
        name:
          type: string
          description: Account label shown in the UI.
          example: Conta Corrente Itaú
        type:
          type: string
          enum:
            - checking
            - savings
            - cash
            - investment
            - credit
          description: Account kind. Drives the reconcile flow and the dashboard categorisation.
        bank_id:
          type:
            - string
            - "null"
          format: uuid
          description: Reference to a `banks` row when the account lives at a bank. `null` for cash / non-bank accounts.
        bank:
          type:
            - object
            - "null"
          properties:
            code:
              type: string
              description: Public COMPE/BACEN code.
              example: "341"
            name:
              type: string
              example: Itaú Unibanco
          required:
            - code
            - name
          description: The account's bank denormalised as its public `{ code, name }`, so reads don't need a second call to `/banks` to translate `bank_id`. `null` when `bank_id` is null.
        agency:
          type:
            - string
            - "null"
        agency_digit:
          type:
            - string
            - "null"
        account_number:
          type:
            - string
            - "null"
        account_digit:
          type:
            - string
            - "null"
        active:
          type: boolean
          description: When false, the account is soft-deactivated (hidden from new forms). Existing transactions stay valid.
        disable_reconcile:
          type: boolean
          description: When true, the manual reconciliation screen skips this account. Useful for non-bank accounts (cash, virtual envelopes).
        metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadados do sistema, gerenciados pela Kobana. Somente leitura. Vínculos com integrações ficam em `metadata.<slug>` = `{ id, payload }` — por exemplo `banking_gateway` (gateway bancário Kobana).
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: "Metadados do usuário: objeto JSON livre."
        created_via:
          type:
            - string
            - "null"
          enum:
            - import
            - dashboard
            - api
            - cli
            - mcp
            - other
            - null
          description: "Channel that created the record: `dashboard`, `import`, `api`, `mcp`, `cli` or `other`. On API calls it is the `X-Request-Source` header. `null` when the change came from the system (scheduled jobs) or predates this tracking."
          example: api
        created_via_other:
          type:
            - string
            - "null"
          description: App that created the record when the channel is `other` (`X-Request-Source-Other`).
          example: null
        created_by_email:
          type:
            - string
            - "null"
          description: E-mail of the person who created the record. `null` for system changes.
          example: maria@empresa.com.br
        updated_via:
          type:
            - string
            - "null"
          enum:
            - import
            - dashboard
            - api
            - cli
            - mcp
            - other
            - null
          description: "Channel that last changed the record: `dashboard`, `import`, `api`, `mcp`, `cli` or `other`. On API calls it is the `X-Request-Source` header. `null` when the change came from the system (scheduled jobs) or predates this tracking."
          example: api
        updated_via_other:
          type:
            - string
            - "null"
          description: App that last changed the record when the channel is `other` (`X-Request-Source-Other`).
          example: null
        updated_by_email:
          type:
            - string
            - "null"
          description: E-mail of the person who last changed the record. `null` for system changes.
          example: maria@empresa.com.br
        created_at:
          type: string
          format: date-time
      required:
        - id
        - company_id
        - name
        - type
        - bank_id
        - bank
        - agency
        - agency_digit
        - account_number
        - account_digit
        - active
        - disable_reconcile
        - metadata
        - custom_metadata
        - created_via
        - created_via_other
        - created_by_email
        - updated_via
        - updated_via_other
        - updated_by_email
        - created_at
      description: "A financial account: a bank account, wallet, investment or card the workspace tracks."
    FinancialAccountDetailV1:
      allOf:
        - $ref: "#/components/schemas/FinancialAccountV1"
        - type: object
          properties:
            balance:
              type: number
              description: Computed current balance in BRL as a signed number, derived from posted (non-pending) transactions. This is the live balance — not the creation seed. Detail-only.
              example: 1530.45
          required:
            - balance
      description: "A financial account as `GET /financial-accounts/{id}` returns it: everything in `FinancialAccountV1` plus its current `balance`."
    FinancialAccountListMeta:
      type: object
      properties:
        next_cursor:
          type:
            - string
            - "null"
          description: Opaque cursor. Pass back as `?cursor=…` to fetch the next page. `null` when there is no next page.
      required:
        - next_cursor
      description: "Pagination metadata of a financial accounts listing: `next_cursor` to pass as `cursor` for the next page (null on the last)."
    FinancialAccountListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/FinancialAccountV1"
        meta:
          $ref: "#/components/schemas/FinancialAccountListMeta"
      required:
        - data
        - meta
      description: "One page of financial accounts: the records in `data` and the pagination in `meta`."
    FinancialAccountCreateRequest:
      type: object
      properties:
        company_id:
          type: string
          format: uuid
          description: "Owning company UUID — required (financial accounts must belong to a company). Must belong to the tenant. Workspace-level accounts (`company_id: null`) exist from imports but are not creatable via v1."
        name:
          type: string
          minLength: 1
          maxLength: 255
        type:
          type: string
          enum:
            - checking
            - savings
            - cash
            - investment
            - credit
        bank_id:
          type:
            - string
            - "null"
          format: uuid
          description: Reference to a `banks` row. `null` for cash / non-bank accounts.
        bank_code:
          type: string
          minLength: 1
          description: "Alternative to `bank_id`: the bank's public COMPE/BACEN code (e.g. `341`), resolved to the catalog bank server-side. Mutually exclusive with `bank_id` — sending both returns 400; an unknown code returns 422."
          example: "341"
        agency:
          type:
            - string
            - "null"
          maxLength: 50
        agency_digit:
          type:
            - string
            - "null"
          maxLength: 5
        account_number:
          type:
            - string
            - "null"
          maxLength: 50
        account_digit:
          type:
            - string
            - "null"
          maxLength: 5
        balance:
          type: number
          description: Opening balance seed (signed number, up to 13 integer digits). Defaults to `0.00`. NOT the live balance — that is computed from transactions.
          example: 0
        disable_reconcile:
          type: boolean
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: "Metadados do usuário: objeto JSON livre. Envie `null` para limpar. Máx. 64 KB serializado, 16 níveis."
      required:
        - company_id
        - name
        - type
      additionalProperties: false
      description: Request body to create a financial account.
    FinancialAccountUpdateRequest:
      type: object
      properties:
        company_id:
          type: string
          format: uuid
          description: "Owning company UUID — required (financial accounts must belong to a company). Must belong to the tenant. Workspace-level accounts (`company_id: null`) exist from imports but are not creatable via v1."
        name:
          type: string
          minLength: 1
          maxLength: 255
        type:
          type: string
          enum:
            - checking
            - savings
            - cash
            - investment
            - credit
        bank_id:
          type:
            - string
            - "null"
          format: uuid
          description: Reference to a `banks` row. `null` for cash / non-bank accounts.
        bank_code:
          type: string
          minLength: 1
          description: "Alternative to `bank_id`: the bank's public COMPE/BACEN code (e.g. `341`), resolved to the catalog bank server-side. Mutually exclusive with `bank_id` — sending both returns 400; an unknown code returns 422."
          example: "341"
        agency:
          type:
            - string
            - "null"
          maxLength: 50
        agency_digit:
          type:
            - string
            - "null"
          maxLength: 5
        account_number:
          type:
            - string
            - "null"
          maxLength: 50
        account_digit:
          type:
            - string
            - "null"
          maxLength: 5
        disable_reconcile:
          type: boolean
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: "Metadados do usuário: objeto JSON livre. Envie `null` para limpar. Máx. 64 KB serializado, 16 níveis."
        active:
          type: boolean
          description: Set `false` to soft-deactivate (existing transactions stay valid).
        custom_metadata_mode:
          type: string
          enum:
            - replace
            - merge
          description: "Como o `custom_metadata` enviado é reconciliado com o valor armazenado: `replace` (padrão) substitui o objeto inteiro, `merge` faz deep merge sobre o valor atual. Aceito apenas na atualização."
      additionalProperties: false
      description: "Request body to update a financial account: send only the fields to change."
    AdjustBalanceRequest:
      type: object
      properties:
        target_balance:
          type: number
          description: The balance the account should have after the adjustment. Signed number (up to 13 integer digits, 2 decimals).
          example: 1500
        category_id:
          type: string
          format: uuid
          description: Category for the adjustment transaction. Its type must match the diff direction (credit for a positive diff, debit for negative) and its company must match the account's.
        occurred_at:
          type: string
          format: date-time
          description: When the adjustment transaction is dated (ISO 8601). Defaults to now.
        description:
          type: string
          maxLength: 500
          description: Overrides the auto-created transaction description (default "Ajuste de saldo").
      required:
        - target_balance
        - category_id
      additionalProperties: false
      description: Request body to adjust the calculated balance of a financial account to a target value — creates the adjusting transaction.
    AdjustBalanceResultV1:
      type: object
      properties:
        transaction_id:
          type: string
          format: uuid
          description: The auto-created adjustment transaction.
        difference:
          type: number
          description: Signed `target_balance − previous_balance`.
          example: 250
        previous_balance:
          type: number
          description: Calculated balance before the adjustment.
          example: 1250
        new_balance:
          type: number
          description: Calculated balance after the adjustment.
          example: 1500
      required:
        - transaction_id
        - difference
        - previous_balance
        - new_balance
      description: "Result of a balance adjustment: the adjusting transaction (`transaction_id`), the difference applied and the balance before and after."
    AccountBalanceV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
          example: 9f1c0b6e-4b23-4a2b-9c3e-6a0c1a2b3c4d
        financial_account_id:
          type: string
          format: uuid
        source:
          type: string
          enum:
            - calculated
            - banking
            - manual
            - imported
            - api
            - mcp
            - cli
          description: Which channel produced this observation. `calculated` is the sum of posted transactions, snapshotted when a bank statement lands; `banking` comes from the Kobana banking gateway; `manual` was typed in the dashboard; `imported` came from a CSV/OFX file; `api`, `mcp` and `cli` identify the client that posted it.
          example: banking
        capture_date:
          type: string
          format: date-time
          description: Instant the balance refers to. This is the ordering key for 'latest balance' — not `created_at`, so an observation from yesterday imported today does not become the most recent one.
          example: 2026-09-10T01:34:41.000Z
        amount:
          type: number
          description: Balance in BRL, signed.
          example: 1530.45
        blocked_amount:
          type:
            - number
            - "null"
          description: Portion held/blocked by the institution. `null` when the source does not report it.
          example: 24
        automatically_invested_amount:
          type:
            - number
            - "null"
          description: Portion under automatic investment. `null` when the source does not report it.
          example: 50
        external_id:
          type:
            - string
            - "null"
          description: Your own id for this observation. Unique per account and source, so re-sending it updates nothing and creates nothing twice.
        tags:
          type: array
          items:
            type: string
          example: []
        notes:
          type:
            - string
            - "null"
        metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadados do sistema, gerenciados pela Kobana. Somente leitura. Vínculos com integrações ficam em `metadata.<slug>` = `{ id, payload }` — por exemplo `banking_gateway` (gateway bancário Kobana).
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: "Metadados do usuário: objeto JSON livre."
        created_at:
          type: string
          format: date-time
      required:
        - id
        - financial_account_id
        - source
        - capture_date
        - amount
        - blocked_amount
        - automatically_invested_amount
        - external_id
        - tags
        - notes
        - metadata
        - custom_metadata
        - created_at
      description: "One recorded balance of a financial account: what a source (the bank, a person, an import) said the balance was at `capture_date`. An observation — it never moves the ledger."
    AccountBalanceListMeta:
      type: object
      properties:
        next_cursor:
          type:
            - string
            - "null"
          description: Opaque cursor for the next page, or `null` on the last page.
      required:
        - next_cursor
      description: "Pagination metadata of a recorded balances listing: `next_cursor` to pass as `cursor` for the next page (null on the last)."
    AccountBalanceListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/AccountBalanceV1"
        meta:
          $ref: "#/components/schemas/AccountBalanceListMeta"
      required:
        - data
        - meta
      description: "One page of recorded balances: the records in `data` and the pagination in `meta`."
    AccountBalanceLatestV1:
      type: object
      properties:
        source:
          type: string
          enum:
            - calculated
            - banking
            - manual
            - imported
            - api
            - mcp
            - cli
        amount:
          type:
            - number
            - "null"
        blocked_amount:
          type:
            - number
            - "null"
        automatically_invested_amount:
          type:
            - number
            - "null"
        capture_date:
          type:
            - string
            - "null"
          format: date-time
        count:
          type: integer
          description: Observations recorded under this source.
      required:
        - source
        - amount
        - blocked_amount
        - automatically_invested_amount
        - capture_date
        - count
      description: The most recent recorded balance of a financial account from one `source`, with how many observations that source has (`count`).
    AccountBalanceLatestListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/AccountBalanceLatestV1"
      required:
        - data
      description: The latest recorded balance of a financial account, one entry per source.
    AccountBalanceCreateRequest:
      type: object
      properties:
        amount:
          type: number
          description: Balance in BRL. Signed — an overdrawn account is negative.
          example: 1530.45
        capture_date:
          type: string
          format: date-time
          description: Instant the balance refers to. Defaults to now.
          example: 2026-09-10T01:34:41.000Z
        source:
          type: string
          enum:
            - api
            - mcp
            - cli
          description: Which client is recording this. Defaults to `api`; send `mcp` or `cli` so the audit trail names the real channel. `manual`, `banking`, `calculated` and `imported` are refused — each has a single producer inside the product and must not be forgeable from a request.
          example: mcp
        blocked_amount:
          type:
            - number
            - "null"
        automatically_invested_amount:
          type:
            - number
            - "null"
        external_id:
          type:
            - string
            - "null"
          maxLength: 255
          description: Your own id for this observation, unique per account and source.
        tags:
          type: array
          items:
            type: string
            maxLength: 255
          maxItems: 50
        notes:
          type:
            - string
            - "null"
          maxLength: 500
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
      required:
        - amount
      additionalProperties: false
      description: Request body to create a recorded balance.
    PayableV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
          example: 8c8b4c4f-2e0e-4f6e-9b8a-2a1c2b3d4e5f
        company_id:
          type: string
          format: uuid
        description:
          type: string
          description: Short label written by the user.
          example: Aluguel — escritório matriz
        amount:
          type: number
          description: A number in BRL, 2 decimal places. Positive — the polarity (cost) is implicit in the resource.
          example: 1500
        interest_amount:
          type:
            - number
            - "null"
          description: Late-payment interest paid at settlement, in BRL. `null` when not applicable.
        discount_amount:
          type:
            - number
            - "null"
          description: Discount obtained at settlement, in BRL. `null` when not applicable.
        ticket_amount:
          type:
            - number
            - "null"
          description: '"Valor liquidado" — actual amount that left the bank. May differ from `nominal_amount` due to bank charges. `null` when not applicable.'
        nominal_amount:
          type: number
          description: "Computed: `amount + (interest_amount ?? 0) - (discount_amount ?? 0)`. Reproduced per request — not persisted."
          example: 1525
        due_date:
          type: string
          format: date-time
          description: When this payable is due. Filterable with range operators (`[gte]`, `[lte]`).
        payment_date:
          type:
            - string
            - "null"
          format: date-time
          description: When this payable was actually paid. `null` while pending. Reflects `status=paid` 1:1.
        competence_date:
          type: string
          format: date-time
          description: Accounting period this charge belongs to (independent of the cash-flow `due_date`).
        type:
          type: string
          enum:
            - normal
            - recurring
            - installment
          description: Billing kind. `recurring` and `installment` link sibling rows via `group_id`.
        status:
          type: string
          enum:
            - pending
            - paid
            - cancelled
          description: 'Raw status. Derive overdue client-side: `status === "pending" AND due_date < today`.'
        person:
          type:
            - object
            - "null"
          properties:
            id:
              type: string
              format: uuid
            name:
              type:
                - string
                - "null"
          required:
            - id
            - name
          description: Related person. `null` when the payable has no person.
        category:
          type:
            - object
            - "null"
          properties:
            id:
              type: string
              format: uuid
            name:
              type:
                - string
                - "null"
          required:
            - id
            - name
          description: Related category. `null` when the payable has no category.
        classification_center:
          type:
            - object
            - "null"
          properties:
            id:
              type: string
              format: uuid
            name:
              type:
                - string
                - "null"
          required:
            - id
            - name
          description: Primary classification center on the row. Splits (rateio) live in `classification_center_allocations[]` on the detail endpoint. `null` when unset.
        financial_account_id:
          type:
            - string
            - "null"
          format: uuid
          description: Settlement account once paid. `null` while pending or for non-account-bound payments.
        expected_account_id:
          type:
            - string
            - "null"
          format: uuid
          description: Projected settlement account, used by the forecast widgets pre-payment.
        payment_method_kind:
          type:
            - string
            - "null"
          enum:
            - bank_billet
            - utility
            - tribute
            - ted
            - pix
            - credit_card
            - debit_card
            - cash
            - check
            - direct_debit
            - other
            - none
            - null
          description: "`utility` (concessionária) and `tribute` are payable-only — they never appear on receivables/transactions."
        operation_kind:
          type:
            - string
            - "null"
          enum:
            - payment
            - transfer
            - null
          description: Kobana operation axis. `payment` settles an existing document (boleto/tribute/utility/Pix copy-paste); `transfer` sends to a payee you identify (TED/Pix key or bank data). NULL for non-Kobana methods (cash/card/cheque). Payable-only.
        payment_approval:
          type:
            - string
            - "null"
          enum:
            - pending_approval
            - partially_approved
            - creation_approved
            - creation_rejected
            - null
          description: "Read-only mirror of the human payment-approval gate. `null` = **official** (never gated, or already approved) — the bill is on the list. The gate is a per-workspace setting (on by default): when it is on, a payable created through this API starts `pending_approval` and is **hidden from this list until a person approves it in the app** (`GET /payables/{id}` still returns it). `partially_approved` = approved by some of the people the workspace requires, still waiting on the rest (and still hidden). `creation_rejected` = blocked forever (fraud/DDA). The two terminal values carry the `creation_` qualifier because `kobana_status` already uses `approved` and `rejected` for the bank's own decision — the two sets are deliberately disjoint. To **list the approval queue**, filter it explicitly — e.g. `?payment_approval=pending_approval` (or `[in]=pending_approval,creation_rejected`); the gated bills then appear. Read-only: this API never approves or rejects — that stays a human action in the app. Detail-only audit stamps (`approved_at`, `rejected_at`, `rejection_reason`, `payment_source`) live on `GET /payables/{id}`."
        document:
          type:
            - string
            - "null"
          description: Free-form reference of the source document (e.g. `NF-12345`, `BOL-001`). Populated by the Fintera / CSV importer and matched by the listing `search` and `documentContains` filters. Distinct from `document_number`.
        document_number:
          type:
            - string
            - "null"
          description: Structured document number — the bare identifier (e.g. `12345`). Mirrors the "Nº de documento" field in the app UI and is the canonical field for manually-entered / edited document numbers. Distinct from the free-form `document`.
        document_date:
          type:
            - string
            - "null"
          format: date-time
        installment_number:
          type:
            - integer
            - "null"
          description: Position within an installment series. `null` for `type=normal`.
        total_installments:
          type:
            - integer
            - "null"
          description: Total positions in the installment series. `null` for `type=normal`.
        parcels_amount:
          type:
            - number
            - "null"
          description: Aggregate value of the whole installment series ("Total parcelado"), in BRL. Mirrored from Fintera. `null` for non-installment rows. Each parcela carries the series total — its own slice is `amount`.
        group_id:
          type:
            - string
            - "null"
          format: uuid
          description: Links sibling rows of a recurring/installment series. `null` for `type=normal`.
        transaction_id:
          type:
            - string
            - "null"
          format: uuid
          description: Settled-payment transaction once `status=paid`. `null` while pending.
        ir_relevant:
          type:
            - boolean
            - "null"
          description: Tax-relevance flag. `null` = unclassified.
        notes:
          type:
            - string
            - "null"
        is_reconciled:
          type: boolean
          description: True when matched against a real bank transaction — a 1:1 manual link (`transaction_id` to a non-auto-created transaction) or membership in an n:m reconciliation group. Settling via the normal pay flow (which generates an auto-created transaction) does NOT set this.
        tax_charges_count:
          type: integer
          minimum: 0
          description: Number of `tax_charges` rows attached. Full list on the detail endpoint.
        classification_center_allocations_count:
          type: integer
          minimum: 0
          description: Number of classification-center allocations (rateio). Full list on the detail endpoint.
        attachments_count:
          type: integer
          minimum: 0
          description: Number of attachments. Full metadata via `/api/v1/attachments`.
        custom_classifications_count:
          type: integer
          minimum: 0
          description: Number of custom-classifier assignments. Full list on the detail endpoint.
        metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadados do sistema, gerenciados pela Kobana. Somente leitura.
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: "Metadados do usuário: objeto JSON livre."
        created_via:
          type:
            - string
            - "null"
          enum:
            - import
            - dashboard
            - api
            - cli
            - mcp
            - other
            - null
          description: "Channel that created the record: `dashboard`, `import`, `api`, `mcp`, `cli` or `other`. On API calls it is the `X-Request-Source` header. `null` when the change came from the system (scheduled jobs) or predates this tracking."
          example: api
        created_via_other:
          type:
            - string
            - "null"
          description: App that created the record when the channel is `other` (`X-Request-Source-Other`).
          example: null
        created_by_email:
          type:
            - string
            - "null"
          description: E-mail of the person who created the record. `null` for system changes.
          example: maria@empresa.com.br
        updated_via:
          type:
            - string
            - "null"
          enum:
            - import
            - dashboard
            - api
            - cli
            - mcp
            - other
            - null
          description: "Channel that last changed the record: `dashboard`, `import`, `api`, `mcp`, `cli` or `other`. On API calls it is the `X-Request-Source` header. `null` when the change came from the system (scheduled jobs) or predates this tracking."
          example: api
        updated_via_other:
          type:
            - string
            - "null"
          description: App that last changed the record when the channel is `other` (`X-Request-Source-Other`).
          example: null
        updated_by_email:
          type:
            - string
            - "null"
          description: E-mail of the person who last changed the record. `null` for system changes.
          example: maria@empresa.com.br
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
          description: Last-write timestamp (ISO 8601). Auto-bumped on every update.
      required:
        - id
        - company_id
        - description
        - amount
        - interest_amount
        - discount_amount
        - ticket_amount
        - nominal_amount
        - due_date
        - payment_date
        - competence_date
        - type
        - status
        - person
        - category
        - classification_center
        - financial_account_id
        - expected_account_id
        - payment_method_kind
        - operation_kind
        - payment_approval
        - document
        - document_number
        - document_date
        - installment_number
        - total_installments
        - parcels_amount
        - group_id
        - transaction_id
        - ir_relevant
        - notes
        - is_reconciled
        - tax_charges_count
        - classification_center_allocations_count
        - attachments_count
        - custom_classifications_count
        - metadata
        - custom_metadata
        - created_via
        - created_via_other
        - created_by_email
        - updated_via
        - updated_via_other
        - updated_by_email
        - created_at
        - updated_at
      description: "A payable (conta a pagar) as listings return it: amount, due date, status (`pending`, `paid`, `cancelled`), supplier, classification and payment method. Overdue is derived: `pending` with `due_date` before today."
    PayableTaxDataV1:
      type: object
      properties:
        tax_code:
          type: string
          description: DARF (código da receita) · GPS (código de pagamento).
        reference_number:
          type: string
          description: DARF · GRU.
        taxpayer_type:
          type: string
          description: DARF · GPS.
        taxpayer_number:
          type: string
        taxpayer_name:
          type: string
        calculation_date:
          type: string
          description: DARF. `yyyy-mm-dd`.
        competence_date:
          type: string
          description: GPS. `yyyy-mm-dd`.
        expire_at:
          type: string
          description: DARF · GRU · GPS. `yyyy-mm-dd`.
        fine_amount:
          type: number
        interest_amount:
          type: number
        gross_income_amount:
          type: number
          description: DARF.
        code:
          type: string
          description: GRU. Barcode (44 digits) or digitable line (48 digits); when omitted, derived from the payable `barcode` at execution.
        total_amount:
          type: number
          description: GRU.
        discount_amount:
          type: number
        other_deduction_amount:
          type: number
        other_addition_amount:
          type: number
        other_entities_amount:
          type: number
          description: GPS.
        monetary_update_amount:
          type: number
        social_security_identification_number:
          type: string
          maxLength: 2
          description: GPS. Tribute identifier at the bank (up to 2 characters).
        contributor_code:
          type: string
          description: FGTS.
        fgts_code:
          type: string
        collection_code:
          type: string
        pis_pasep_number:
          type: string
        jam_percentage:
          type: number
        connectivity_seal:
          type: string
      description: "The data of the tax guide (DARF, GPS, FGTS…) a payable pays: taxpayer, codes, competence and the amounts that make up the total."
    PayableClassificationCenterAllocationV1:
      type: object
      properties:
        classification_center:
          type: object
          properties:
            id:
              type: string
              format: uuid
            name:
              type:
                - string
                - "null"
          required:
            - id
            - name
          description: The classification center. `name` is `null` if the join is unavailable.
        percentage:
          type:
            - number
            - "null"
          description: Share of the total, 0–100 with up to 2 decimals. `null` when the row was created amount-only.
        amount:
          type:
            - number
            - "null"
          description: Allocated amount in BRL. `null` when the row was created percentage-only.
      required:
        - classification_center
        - percentage
        - amount
      description: "How a payable is split across classification centers: one center and its share."
    PayableCustomClassificationV1:
      type: object
      properties:
        custom_classifier:
          type: object
          properties:
            id:
              type: string
              format: uuid
            name:
              type:
                - string
                - "null"
          required:
            - id
            - name
          description: The dimension. `name` is `null` if the join is unavailable.
        custom_classifier_value:
          type:
            - object
            - "null"
          properties:
            id:
              type: string
              format: uuid
            name:
              type:
                - string
                - "null"
          required:
            - id
            - name
          description: Assigned list value (id + label). `null` for scalar dimensions (see `raw_value`) or when the join is unavailable.
        raw_value:
          type:
            - string
            - "null"
          description: Free value for scalar dimensions; `null` for list dimensions.
        percentage:
          type:
            - number
            - "null"
          description: Share of the total, 0–100 with up to 2 decimals. `null` for native single-select assignments.
        amount:
          type:
            - number
            - "null"
          description: Allocated amount in BRL. `null` for native single-select assignments.
      required:
        - custom_classifier
        - custom_classifier_value
        - raw_value
        - percentage
        - amount
      description: A custom classifier value applied to a payable.
    PayableTaxChargeV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
        tax:
          type: object
          properties:
            id:
              type: string
              format: uuid
            name:
              type:
                - string
                - "null"
          required:
            - id
            - name
          description: The tax. `name` is `null` if the join is unavailable.
        amount:
          type: number
          description: Charge amount in BRL.
        withholding:
          type: boolean
          description: "`true` = amount is withheld (deducted); `false` = informational."
      required:
        - id
        - tax
        - amount
        - withholding
      description: A tax withheld or charged on a payable.
    PayableReconciliationV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
        source:
          type: string
          description: 'Origin of the reconciliation group: `"manual"` (user-driven) or `"fintera"` (imported).'
        transaction_ids:
          type: array
          items:
            type: string
            format: uuid
          description: IDs of the bank transactions in the n:m group.
        billing_ids:
          type: array
          items:
            type: string
            format: uuid
          description: IDs of the billings (payables/receivables) in the group, including this payable.
      required:
        - id
        - source
        - transaction_ids
        - billing_ids
      description: "The reconciliation a payable is part of: its source and the transactions and billings it links."
    PayableReceiptEmailV1:
      type: object
      properties:
        label:
          type:
            - string
            - "null"
          maxLength: 50
          description: Free label, e.g. "Contabilidade".
        address:
          type:
            - string
            - "null"
          description: The e-mail address.
      required:
        - label
        - address
      description: An email address the payment receipt of a payable is sent to, with its label.
    PayableDetailV1:
      allOf:
        - $ref: "#/components/schemas/PayableV1"
        - type: object
          properties:
            barcode:
              type:
                - string
                - "null"
              description: Boleto / tributo / concessionária barcode (linha digitável). Register-only.
            bank:
              type: object
              properties:
                code:
                  type:
                    - string
                    - "null"
                  description: Beneficiary bank COMPE. String — leading zeros are significant (`001`).
                ispb:
                  type:
                    - string
                    - "null"
                  description: Beneficiary bank ISPB (8 digits, string).
              required:
                - code
                - ispb
            bank_account:
              type: object
              properties:
                agency:
                  type:
                    - string
                    - "null"
                agency_digit:
                  type:
                    - string
                    - "null"
                number:
                  type:
                    - string
                    - "null"
                digit:
                  type:
                    - string
                    - "null"
              required:
                - agency
                - agency_digit
                - number
                - digit
            pix:
              type: object
              properties:
                key:
                  type:
                    - string
                    - "null"
                  description: Beneficiary PIX key (key-based transfer).
                key_type:
                  type:
                    - string
                    - "null"
                  enum:
                    - cpf
                    - cnpj
                    - email
                    - phone
                    - random
                    - null
                copia_e_cola:
                  type:
                    - string
                    - "null"
                  description: PIX copia-e-cola (QR payload).
              required:
                - key
                - key_type
                - copia_e_cola
            transfer_purpose:
              type:
                - string
                - "null"
              description: Purpose of a TED / account PIX transfer.
            judicial_deposit_number:
              type:
                - string
                - "null"
              description: "Judicial deposit identifier (digits only). Set → TED judicial: `bank.code` is the depositary bank; `bank_account` is not used."
            tax_kind:
              type:
                - string
                - "null"
              enum:
                - itbi
                - icms
                - iss
                - iptu
                - fgts
                - dare
                - darf
                - gru
                - gps
                - null
              description: Tribute kind when `payment_method_kind = tribute`. The barcode goes in `barcode`.
            tax_data:
              allOf:
                - $ref: "#/components/schemas/PayableTaxDataV1"
                - type:
                    - object
                    - "null"
                  description: Structured guia fields, the alternative to `barcode`. `null` when the barcode was used.
            beneficiary:
              type: object
              properties:
                name:
                  type:
                    - string
                    - "null"
                  description: Transfer beneficiary name. May differ from the payable's `person` (the bill owner isn't always the payee).
                document:
                  type:
                    - string
                    - "null"
                  description: Beneficiary CPF/CNPJ. Register-relaxed (no mask enforced).
              required:
                - name
                - document
            scheduled_to:
              type:
                - string
                - "null"
              format: date-time
              description: Scheduled payment date (ISO 8601). Suggested default is `due_date`.
            receipt_emails:
              type: array
              items:
                $ref: "#/components/schemas/PayableReceiptEmailV1"
              description: "Who receives the payment receipt (comprovante) by e-mail once one is attached to this payable — the receipt travels as an attachment. Scoped to this payable and external by design: it is separate from (and additional to) the workspace-wide recipient list, which only accepts workspace members. Empty when nobody was registered."
            kobana_payment_uid:
              type:
                - string
                - "null"
              description: "Execution-state mirror: linked Kobana payment `uid`. Read-only — populated by the execution flow, never via this API."
            kobana_status:
              type:
                - string
                - "null"
              description: Execution-state mirror of the Kobana payment status enum. Read-only.
            kobana_status_updated_at:
              type:
                - string
                - "null"
              format: date-time
              description: When `kobana_status` was last mirrored (ISO 8601). Read-only.
            approved_at:
              type:
                - string
                - "null"
              format: date-time
              description: When the payable was approved (ISO 8601). Read-only. `null` unless `payment_approval = creation_approved`.
            rejected_at:
              type:
                - string
                - "null"
              format: date-time
              description: When the payable was rejected (ISO 8601). Read-only. `null` unless `payment_approval = creation_rejected`.
            rejection_reason:
              type:
                - string
                - "null"
              description: Audit note recorded when the payment was rejected. Read-only.
            payment_source:
              type:
                - string
                - "null"
              enum:
                - manual
                - csv_import
                - dda
                - ai_agent
                - fiscal_document
                - inbox
                - null
              description: Origin channel of the payable — the anti-fraud context of the approval screen (`manual` = created in the app/API, `csv_import` = the CSV importer, `fiscal_document` = generated from an instalment of a received invoice, `inbox` = created by someone confirming a document that arrived in the document inbox). Read-only; `null` for rows predating the feature.
            classification_center_allocations:
              type: array
              items:
                $ref: "#/components/schemas/PayableClassificationCenterAllocationV1"
              description: Rateio rows (classification-center allocations). Empty when the payable has no split.
            custom_classifications:
              type: array
              items:
                $ref: "#/components/schemas/PayableCustomClassificationV1"
              description: Custom-classifier assignments (one per dimension for native single-select). Empty when none.
            tax_charges:
              type: array
              items:
                $ref: "#/components/schemas/PayableTaxChargeV1"
              description: Withholding/tax charges attached to the payable. Empty when none.
            reconciliation:
              allOf:
                - $ref: "#/components/schemas/PayableReconciliationV1"
                - type:
                    - object
                    - "null"
                  description: The reconciliation group this payable belongs to, or `null` when not reconciled.
          required:
            - barcode
            - bank
            - bank_account
            - pix
            - transfer_purpose
            - judicial_deposit_number
            - tax_kind
            - tax_data
            - beneficiary
            - scheduled_to
            - receipt_emails
            - kobana_payment_uid
            - kobana_status
            - kobana_status_updated_at
            - approved_at
            - rejected_at
            - rejection_reason
            - payment_source
            - classification_center_allocations
            - custom_classifications
            - tax_charges
            - reconciliation
      description: "A payable as `GET /payables/{id}` returns it: everything in `PayableV1` plus the classification-center split, custom classifications, taxes, reconciliation and the payment-approval audit stamps."
    PayableListMeta:
      type: object
      properties:
        next_cursor:
          type:
            - string
            - "null"
          description: Opaque cursor. Pass back as `?cursor=…` to fetch the next page. `null` when there is no next page.
      required:
        - next_cursor
      description: "Pagination metadata of a payables listing: `next_cursor` to pass as `cursor` for the next page (null on the last)."
    PayableListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/PayableV1"
        meta:
          $ref: "#/components/schemas/PayableListMeta"
      required:
        - data
        - meta
      description: "One page of payables: the records in `data` and the pagination in `meta`."
    PayableClassificationCenterSplitInput:
      type: object
      properties:
        classification_center:
          type: object
          properties:
            id:
              type: string
              format: uuid
          required:
            - id
          additionalProperties: false
        percentage:
          type: number
          description: Share of the total (0–100). Provide `percentage` and/or `amount`; the server normalises the pair.
        amount:
          type: number
          description: Allocated amount in BRL, in BRL (`1920.0`). Provide `percentage` and/or `amount`. A JSON number is still accepted for backward compatibility; the string is canonical and is what the reads return.
      required:
        - classification_center
      description: One line of the classification-center split of a payable, as sent on create or update.
    PayableCustomClassificationInput:
      type: object
      properties:
        custom_classifier:
          type: object
          properties:
            id:
              type: string
              format: uuid
          required:
            - id
          additionalProperties: false
        custom_classifier_value:
          type: object
          properties:
            id:
              type: string
              format: uuid
            name:
              type: string
              minLength: 1
              maxLength: 255
          additionalProperties: false
          description: "Assigned value: `{id}` for an existing value, or `{name}` to find-or-create it under the dimension."
        raw_value:
          type: string
          maxLength: 2000
          description: Free value for a scalar dimension. Use instead of a value id/name.
        percentage:
          type: number
          description: Optional rateio share (0–100). Omit for native single-select (stored as null).
        amount:
          type: number
          description: Optional rateio amount in BRL, in BRL. Omit for native single-select.
      required:
        - custom_classifier
      description: A custom classifier value to apply to a payable, as sent on create or update.
    PayableTaxChargeInput:
      type: object
      properties:
        tax:
          type: object
          properties:
            id:
              type: string
              format: uuid
          required:
            - id
          additionalProperties: false
        amount:
          type: number
          description: Charge amount in BRL, in BRL (`48.0`).
        withholding:
          type: boolean
          description: Defaults to `true` (withheld). `false` marks it informational.
      required:
        - tax
        - amount
      description: A tax to withhold or charge on a payable, as sent on create or update.
    PayableCreateRequest:
      type: object
      properties:
        company_id:
          type: string
          format: uuid
          description: Owning company UUID. Must belong to the tenant.
        description:
          type: string
          minLength: 1
          maxLength: 500
        amount:
          type: number
          description: A number in BRL (up to 13 integer digits, 2 decimals). Always positive — cost polarity is implicit.
          example: 1500
        due_date:
          type: string
          format: date-time
          description: When this payable is due (ISO 8601).
        competence_date:
          type: string
          format: date-time
          description: Accounting period (ISO 8601). Defaults to `due_date` when omitted.
        type:
          type: string
          enum:
            - normal
            - recurring
            - installment
          description: Defaults to `normal`. `recurring` requires `recurrence_period` and stores the series ahead up to the workspace's provisioning horizon (default 12 months) as rows sharing `group_id`; `installment` requires `total_installments` (2–120). A title that belongs to a series keeps its `type`; a standalone `normal` title may become `recurring`.
        person:
          type:
            - object
            - "null"
          properties:
            id:
              type: string
              format: uuid
            name:
              type: string
              minLength: 1
              maxLength: 255
          additionalProperties: false
          description: "`{id}` for an existing person, or `{name}` to find-or-create one with `supplier` polarity, scoped to (workspace, company). Never both."
        category:
          type:
            - object
            - "null"
          properties:
            id:
              type: string
              format: uuid
            name:
              type: string
              minLength: 1
              maxLength: 255
          additionalProperties: false
          description: "`{id}` for an existing category, or `{name}` to find-or-create one with `debit` polarity. Never both."
        classification_center:
          type:
            - object
            - "null"
          properties:
            id:
              type: string
              format: uuid
            name:
              type: string
              minLength: 1
              maxLength: 255
          additionalProperties: false
          description: "`{id}` for an existing classification center, or `{name}` to find-or-create one with `cost` polarity — matches a center typed `cost` or `both`; creates as `cost` when no match. Never both."
        payment_method_kind:
          type:
            - string
            - "null"
          enum:
            - bank_billet
            - utility
            - tribute
            - ted
            - pix
            - credit_card
            - debit_card
            - cash
            - check
            - direct_debit
            - other
            - none
            - null
        operation_kind:
          type:
            - string
            - "null"
          enum:
            - payment
            - transfer
            - null
        financial_account_id:
          type:
            - string
            - "null"
          format: uuid
          description: Optional pre-set settlement account. Must belong to the tenant and match `company_id`.
        expected_account_id:
          type:
            - string
            - "null"
          format: uuid
          description: Projected settlement account (forecast). Independent of `financial_account_id`.
        document:
          type:
            - string
            - "null"
          maxLength: 100
          description: Free-form reference of the source document (e.g. `NF-12345`, `BOL-001`); what the `search` / `documentContains` filters match. Distinct from `document_number`.
        document_date:
          type:
            - string
            - "null"
          format: date-time
        document_number:
          type:
            - string
            - "null"
          maxLength: 100
          description: Structured document number — the bare identifier (e.g. `12345`). Mirrors the "Nº de documento" field in the app UI; the canonical field for manually-entered document numbers. Distinct from the free-form `document`.
        notes:
          type:
            - string
            - "null"
          maxLength: 2000
        ir_relevant:
          type:
            - boolean
            - "null"
        recurrence_period:
          type: string
          maxLength: 50
          description: Required for `type=recurring` and rejected for any other type (`weekly` | `biweekly` | `monthly` | `quarterly` | `yearly`).
        total_installments:
          type: integer
          minimum: 2
          maximum: 120
          description: Required for `type=installment` and rejected for any other type — number of parcelas (2–120). A title that belongs to a series keeps its `type` and `total_installments`.
        classification_centers:
          type: array
          items:
            $ref: "#/components/schemas/PayableClassificationCenterSplitInput"
          description: Rateio. Replace-all set; the server validates Σ against `amount` (0.01 tolerance). Spread across installments.
        custom_classifications:
          type: array
          items:
            $ref: "#/components/schemas/PayableCustomClassificationInput"
          description: Custom-classifier assignments. Replace-all set; one value per dimension for native single-select. Replicated onto every installment.
        tax_charges:
          type: array
          items:
            $ref: "#/components/schemas/PayableTaxChargeInput"
          description: Withholding/tax charges. Replace-all set. Attached to the first record of a series.
        barcode:
          type:
            - string
            - "null"
          maxLength: 255
        bank:
          type:
            - object
            - "null"
          properties:
            code:
              type:
                - string
                - "null"
              maxLength: 30
              description: Beneficiary bank COMPE. String — leading zeros significant.
            ispb:
              type:
                - string
                - "null"
              maxLength: 8
          additionalProperties: false
        bank_account:
          type:
            - object
            - "null"
          properties:
            agency:
              type:
                - string
                - "null"
              maxLength: 50
            agency_digit:
              type:
                - string
                - "null"
              maxLength: 5
            number:
              type:
                - string
                - "null"
              maxLength: 50
            digit:
              type:
                - string
                - "null"
              maxLength: 5
          additionalProperties: false
        pix:
          type:
            - object
            - "null"
          properties:
            key:
              type:
                - string
                - "null"
              maxLength: 255
            key_type:
              type:
                - string
                - "null"
              enum:
                - cpf
                - cnpj
                - email
                - phone
                - random
                - null
            copia_e_cola:
              type:
                - string
                - "null"
          additionalProperties: false
        transfer_purpose:
          type:
            - string
            - "null"
          maxLength: 100
        judicial_deposit_number:
          type:
            - string
            - "null"
          pattern: ^\d{1,20}$
          description: Judicial deposit identifier (digits only, up to 20). Set with `operation_kind = transfer` + `payment_method_kind = ted`; `bank.code` is the depositary bank and no `bank_account` is needed.
        tax_kind:
          type:
            - string
            - "null"
          enum:
            - itbi
            - icms
            - iss
            - iptu
            - fgts
            - dare
            - darf
            - gru
            - gps
            - null
          description: Tribute kind. Set with `payment_method_kind = tribute`; the barcode goes in `barcode`.
        tax_data:
          allOf:
            - $ref: "#/components/schemas/PayableTaxDataV1"
            - type:
                - object
                - "null"
              description: Structured guia fields, the alternative to `barcode`.
        beneficiary:
          type:
            - object
            - "null"
          properties:
            name:
              type:
                - string
                - "null"
              maxLength: 255
              description: Transfer beneficiary name. May differ from the payable's `person`.
            document:
              type:
                - string
                - "null"
              maxLength: 20
              description: Beneficiary CPF/CNPJ.
          additionalProperties: false
        scheduled_to:
          type:
            - string
            - "null"
          format: date-time
          description: Scheduled payment date (ISO 8601). Suggested default is `due_date`.
        receipt_emails:
          type:
            - array
            - "null"
          items:
            type: object
            properties:
              label:
                type: string
                maxLength: 50
              address:
                type: string
                maxLength: 80
                format: email
            additionalProperties: false
          maxItems: 50
          description: Replace-all list of receipt (comprovante) recipients for this payable — they receive the receipt as an attachment once one is attached. Omit to leave the current list untouched; send `[]` or `null` to clear it. Additional to the workspace-wide recipient list, never a replacement for it.
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: "Metadados do usuário: objeto JSON livre. Envie `null` para limpar. Máx. 64 KB serializado, 16 níveis."
      required:
        - company_id
        - description
        - amount
        - due_date
      additionalProperties: false
      description: Request body to create a payable.
    PayableUpdateRequest:
      type: object
      properties:
        description:
          type: string
          minLength: 1
          maxLength: 500
        amount:
          type: number
          description: A number in BRL (up to 13 integer digits, 2 decimals). Always positive — cost polarity is implicit.
          example: 1500
        due_date:
          type: string
          format: date-time
          description: When this payable is due (ISO 8601).
        competence_date:
          type: string
          format: date-time
          description: Accounting period (ISO 8601). Defaults to `due_date` when omitted.
        type:
          type: string
          enum:
            - normal
            - recurring
            - installment
          description: Defaults to `normal`. `recurring` requires `recurrence_period` and stores the series ahead up to the workspace's provisioning horizon (default 12 months) as rows sharing `group_id`; `installment` requires `total_installments` (2–120). A title that belongs to a series keeps its `type`; a standalone `normal` title may become `recurring`.
        person:
          type:
            - object
            - "null"
          properties:
            id:
              type: string
              format: uuid
            name:
              type: string
              minLength: 1
              maxLength: 255
          additionalProperties: false
          description: "`{id}` for an existing person, or `{name}` to find-or-create one with `supplier` polarity, scoped to (workspace, company). Never both."
        category:
          type:
            - object
            - "null"
          properties:
            id:
              type: string
              format: uuid
            name:
              type: string
              minLength: 1
              maxLength: 255
          additionalProperties: false
          description: "`{id}` for an existing category, or `{name}` to find-or-create one with `debit` polarity. Never both."
        classification_center:
          type:
            - object
            - "null"
          properties:
            id:
              type: string
              format: uuid
            name:
              type: string
              minLength: 1
              maxLength: 255
          additionalProperties: false
          description: "`{id}` for an existing classification center, or `{name}` to find-or-create one with `cost` polarity — matches a center typed `cost` or `both`; creates as `cost` when no match. Never both."
        payment_method_kind:
          type:
            - string
            - "null"
          enum:
            - bank_billet
            - utility
            - tribute
            - ted
            - pix
            - credit_card
            - debit_card
            - cash
            - check
            - direct_debit
            - other
            - none
            - null
        operation_kind:
          type:
            - string
            - "null"
          enum:
            - payment
            - transfer
            - null
        financial_account_id:
          type:
            - string
            - "null"
          format: uuid
          description: Optional pre-set settlement account. Must belong to the tenant and match `company_id`.
        expected_account_id:
          type:
            - string
            - "null"
          format: uuid
          description: Projected settlement account (forecast). Independent of `financial_account_id`.
        document:
          type:
            - string
            - "null"
          maxLength: 100
          description: Free-form reference of the source document (e.g. `NF-12345`, `BOL-001`); what the `search` / `documentContains` filters match. Distinct from `document_number`.
        document_date:
          type:
            - string
            - "null"
          format: date-time
        document_number:
          type:
            - string
            - "null"
          maxLength: 100
          description: Structured document number — the bare identifier (e.g. `12345`). Mirrors the "Nº de documento" field in the app UI; the canonical field for manually-entered document numbers. Distinct from the free-form `document`.
        notes:
          type:
            - string
            - "null"
          maxLength: 2000
        ir_relevant:
          type:
            - boolean
            - "null"
        recurrence_period:
          type: string
          maxLength: 50
          description: Required for `type=recurring` and rejected for any other type (`weekly` | `biweekly` | `monthly` | `quarterly` | `yearly`).
        total_installments:
          type: integer
          minimum: 2
          maximum: 120
          description: Required for `type=installment` and rejected for any other type — number of parcelas (2–120). A title that belongs to a series keeps its `type` and `total_installments`.
        classification_centers:
          type: array
          items:
            $ref: "#/components/schemas/PayableClassificationCenterSplitInput"
          description: Rateio. Replace-all set; the server validates Σ against `amount` (0.01 tolerance). Spread across installments.
        custom_classifications:
          type: array
          items:
            $ref: "#/components/schemas/PayableCustomClassificationInput"
          description: Custom-classifier assignments. Replace-all set; one value per dimension for native single-select. Replicated onto every installment.
        tax_charges:
          type: array
          items:
            $ref: "#/components/schemas/PayableTaxChargeInput"
          description: Withholding/tax charges. Replace-all set. Attached to the first record of a series.
        barcode:
          type:
            - string
            - "null"
          maxLength: 255
        bank:
          type:
            - object
            - "null"
          properties:
            code:
              type:
                - string
                - "null"
              maxLength: 30
              description: Beneficiary bank COMPE. String — leading zeros significant.
            ispb:
              type:
                - string
                - "null"
              maxLength: 8
          additionalProperties: false
        bank_account:
          type:
            - object
            - "null"
          properties:
            agency:
              type:
                - string
                - "null"
              maxLength: 50
            agency_digit:
              type:
                - string
                - "null"
              maxLength: 5
            number:
              type:
                - string
                - "null"
              maxLength: 50
            digit:
              type:
                - string
                - "null"
              maxLength: 5
          additionalProperties: false
        pix:
          type:
            - object
            - "null"
          properties:
            key:
              type:
                - string
                - "null"
              maxLength: 255
            key_type:
              type:
                - string
                - "null"
              enum:
                - cpf
                - cnpj
                - email
                - phone
                - random
                - null
            copia_e_cola:
              type:
                - string
                - "null"
          additionalProperties: false
        transfer_purpose:
          type:
            - string
            - "null"
          maxLength: 100
        judicial_deposit_number:
          type:
            - string
            - "null"
          pattern: ^\d{1,20}$
          description: Judicial deposit identifier (digits only, up to 20). Set with `operation_kind = transfer` + `payment_method_kind = ted`; `bank.code` is the depositary bank and no `bank_account` is needed.
        tax_kind:
          type:
            - string
            - "null"
          enum:
            - itbi
            - icms
            - iss
            - iptu
            - fgts
            - dare
            - darf
            - gru
            - gps
            - null
          description: Tribute kind. Set with `payment_method_kind = tribute`; the barcode goes in `barcode`.
        tax_data:
          allOf:
            - $ref: "#/components/schemas/PayableTaxDataV1"
            - type:
                - object
                - "null"
              description: Structured guia fields, the alternative to `barcode`.
        beneficiary:
          type:
            - object
            - "null"
          properties:
            name:
              type:
                - string
                - "null"
              maxLength: 255
              description: Transfer beneficiary name. May differ from the payable's `person`.
            document:
              type:
                - string
                - "null"
              maxLength: 20
              description: Beneficiary CPF/CNPJ.
          additionalProperties: false
        scheduled_to:
          type:
            - string
            - "null"
          format: date-time
          description: Scheduled payment date (ISO 8601). Suggested default is `due_date`.
        receipt_emails:
          type:
            - array
            - "null"
          items:
            type: object
            properties:
              label:
                type: string
                maxLength: 50
              address:
                type: string
                maxLength: 80
                format: email
            additionalProperties: false
          maxItems: 50
          description: Replace-all list of receipt (comprovante) recipients for this payable — they receive the receipt as an attachment once one is attached. Omit to leave the current list untouched; send `[]` or `null` to clear it. Additional to the workspace-wide recipient list, never a replacement for it.
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: "Metadados do usuário: objeto JSON livre. Envie `null` para limpar. Máx. 64 KB serializado, 16 níveis."
        status:
          type: string
          enum:
            - pending
            - paid
            - cancelled
          description: Settle (`paid`), revert (`pending`) or cancel (`cancelled`). `draft` is never accepted.
        payment_date:
          type:
            - string
            - "null"
          format: date-time
          description: Settlement date (ISO 8601). Required when moving to `status=paid`.
        interest_amount:
          type:
            - number
            - "null"
          description: Late-payment interest paid at settlement (BRL), in BRL.
        discount_amount:
          type:
            - number
            - "null"
          description: Discount obtained at settlement (BRL), in BRL.
        ticket_amount:
          type:
            - number
            - "null"
          description: '"Valor liquidado" — the actual amount that left the bank (BRL), in BRL.'
        custom_metadata_mode:
          type: string
          enum:
            - replace
            - merge
          description: "Como o `custom_metadata` enviado é reconciliado com o valor armazenado: `replace` (padrão) substitui o objeto inteiro, `merge` faz deep merge sobre o valor atual. Aceito apenas na atualização."
      additionalProperties: false
      description: "Request body to update a payable: send only the fields to change."
    ReceivableV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
          example: 8c8b4c4f-2e0e-4f6e-9b8a-2a1c2b3d4e5f
        company_id:
          type: string
          format: uuid
        description:
          type: string
          description: Short label written by the user.
          example: "NF #1234 — venda janeiro"
        amount:
          type: number
          description: Headline value in BRL. Positive.
          example: 1500
        interest_amount:
          type:
            - number
            - "null"
          description: Late-payment interest collected at receive time, in BRL. `null` when not applicable.
        discount_amount:
          type:
            - number
            - "null"
          description: Discount granted at receive time, in BRL. `null` when not applicable.
        ticket_amount:
          type:
            - number
            - "null"
          description: '"Valor liquidado" — actual amount that hit the bank. May differ from `nominal_amount` due to processor fees. `null` when not applicable.'
        nominal_amount:
          type: number
          description: "Computed: `amount + (interest_amount ?? 0) - (discount_amount ?? 0)`. Reproduced per request — not persisted."
          example: 1525
        due_date:
          type: string
          format: date-time
        payment_date:
          type:
            - string
            - "null"
          format: date-time
          description: When this receivable was actually received. Column name kept as `payment_date` to match the schema; semantically "received at". `null` while pending. Reflects `status=received` 1:1.
        competence_date:
          type: string
          format: date-time
        type:
          type: string
          enum:
            - normal
            - recurring
            - installment
        status:
          type: string
          enum:
            - pending
            - received
            - cancelled
          description: 'Raw status. Derive overdue client-side: `status === "pending" AND due_date < today`.'
        person:
          type:
            - object
            - "null"
          properties:
            id:
              type: string
              format: uuid
            name:
              type:
                - string
                - "null"
          required:
            - id
            - name
          description: Related person. `null` when the receivable has no person.
        category:
          type:
            - object
            - "null"
          properties:
            id:
              type: string
              format: uuid
            name:
              type:
                - string
                - "null"
          required:
            - id
            - name
          description: Related category. `null` when the receivable has no category.
        classification_center:
          type:
            - object
            - "null"
          properties:
            id:
              type: string
              format: uuid
            name:
              type:
                - string
                - "null"
          required:
            - id
            - name
          description: Primary classification center on the row. Splits (rateio) live in `classification_center_allocations[]` on the detail endpoint. `null` when unset.
        financial_account_id:
          type:
            - string
            - "null"
          format: uuid
        expected_account_id:
          type:
            - string
            - "null"
          format: uuid
          description: Projected settlement account pre-receive, used by forecast widgets.
        payment_method_kind:
          type:
            - string
            - "null"
          enum:
            - bank_billet
            - ted
            - pix
            - credit_card
            - debit_card
            - cash
            - check
            - direct_debit
            - other
            - none
            - null
        document:
          type:
            - string
            - "null"
          description: Free-form reference of the source document (e.g. `NF-12345`, `BOL-001`). Populated by the Fintera / CSV importer and matched by the listing `search` and `documentContains` filters. Distinct from `document_number`.
        document_number:
          type:
            - string
            - "null"
          description: Structured document number — the bare identifier (e.g. `12345`). Mirrors the "Nº de documento" field in the app UI and is the canonical field for manually-entered / edited document numbers. Distinct from the free-form `document`.
        document_date:
          type:
            - string
            - "null"
          format: date-time
        installment_number:
          type:
            - integer
            - "null"
        total_installments:
          type:
            - integer
            - "null"
        parcels_amount:
          type:
            - number
            - "null"
          description: Aggregate value of the whole installment series ("Total parcelado"), in BRL. Mirrored from Fintera. `null` for non-installment rows. Each parcela carries the series total — its own slice is `amount`.
        group_id:
          type:
            - string
            - "null"
          format: uuid
        transaction_id:
          type:
            - string
            - "null"
          format: uuid
          description: Settled-receipt transaction once `status=received`. `null` while pending.
        ir_relevant:
          type:
            - boolean
            - "null"
        notes:
          type:
            - string
            - "null"
        is_reconciled:
          type: boolean
          description: True when matched against a real bank transaction — a 1:1 manual link (`transaction_id` to a non-auto-created transaction) or membership in an n:m reconciliation group. Receiving via the normal flow (auto-created transaction) does NOT set this.
        charge:
          type:
            - object
            - "null"
          properties:
            uid:
              type:
                - string
                - "null"
            kind:
              type:
                - string
                - "null"
              enum:
                - bank_billet
                - pix
                - null
              description: Which rail the charge was issued on. `null` when never charged.
            status:
              type:
                - string
                - "null"
              description: "**Commercial** axis (`opened`, `paid`, `overdue`, `canceled`, `generation_failed`, …). Free string — Kobana owns the vocabulary and may add values. ⚠️ Do not branch on this alone: an `opened` charge whose registration has not confirmed is not payable. Use `state`."
            registration_status:
              type:
                - string
                - "null"
              description: "**Registration** axis — the state at the financial institution (`pending`, `confirmed`, `skipped`, `rejected`, `failed`). Free string. This is the axis that says whether the charge can be handed to the payer."
            state:
              type:
                - string
                - "null"
              enum:
                - registering
                - registered
                - overdue
                - paid
                - registration_rejected
                - registration_failed
                - generation_failed
                - canceled
                - null
              description: "Computed by Finance (not a Kobana field, and not persisted): the two axes above collapsed into the one state to branch on. A terminal commercial state wins; below it the registration decides between `registering` and `registered`. `registered` is the first state in which the charge can be delivered to the payer — before it, a bank slip does not exist at the bank and a Pix has no QR code."
            status_updated_at:
              type:
                - string
                - "null"
              format: date-time
              description: When the charge mirror was last written (ISO 8601).
          required:
            - uid
            - kind
            - status
            - registration_status
            - state
            - status_updated_at
          description: Charge mirror. `null` when never charged — its presence is what "already charged" means. Read-only — populated when a charge is issued, never through the write endpoints.
        tax_charges_count:
          type: integer
          minimum: 0
        classification_center_allocations_count:
          type: integer
          minimum: 0
        attachments_count:
          type: integer
          minimum: 0
        custom_classifications_count:
          type: integer
          minimum: 0
          description: Number of custom-classifier assignments. Full list on the detail endpoint.
        metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadados do sistema, gerenciados pela Kobana. Somente leitura.
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: "Metadados do usuário: objeto JSON livre."
        created_via:
          type:
            - string
            - "null"
          enum:
            - import
            - dashboard
            - api
            - cli
            - mcp
            - other
            - null
          description: "Channel that created the record: `dashboard`, `import`, `api`, `mcp`, `cli` or `other`. On API calls it is the `X-Request-Source` header. `null` when the change came from the system (scheduled jobs) or predates this tracking."
          example: api
        created_via_other:
          type:
            - string
            - "null"
          description: App that created the record when the channel is `other` (`X-Request-Source-Other`).
          example: null
        created_by_email:
          type:
            - string
            - "null"
          description: E-mail of the person who created the record. `null` for system changes.
          example: maria@empresa.com.br
        updated_via:
          type:
            - string
            - "null"
          enum:
            - import
            - dashboard
            - api
            - cli
            - mcp
            - other
            - null
          description: "Channel that last changed the record: `dashboard`, `import`, `api`, `mcp`, `cli` or `other`. On API calls it is the `X-Request-Source` header. `null` when the change came from the system (scheduled jobs) or predates this tracking."
          example: api
        updated_via_other:
          type:
            - string
            - "null"
          description: App that last changed the record when the channel is `other` (`X-Request-Source-Other`).
          example: null
        updated_by_email:
          type:
            - string
            - "null"
          description: E-mail of the person who last changed the record. `null` for system changes.
          example: maria@empresa.com.br
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
          description: Last-write timestamp (ISO 8601). Auto-bumped on every update.
      required:
        - id
        - company_id
        - description
        - amount
        - interest_amount
        - discount_amount
        - ticket_amount
        - nominal_amount
        - due_date
        - payment_date
        - competence_date
        - type
        - status
        - person
        - category
        - classification_center
        - financial_account_id
        - expected_account_id
        - payment_method_kind
        - document
        - document_number
        - document_date
        - installment_number
        - total_installments
        - parcels_amount
        - group_id
        - transaction_id
        - ir_relevant
        - notes
        - is_reconciled
        - charge
        - tax_charges_count
        - classification_center_allocations_count
        - attachments_count
        - custom_classifications_count
        - metadata
        - custom_metadata
        - created_via
        - created_via_other
        - created_by_email
        - updated_via
        - updated_via_other
        - updated_by_email
        - created_at
        - updated_at
      description: "A receivable (conta a receber) as listings return it: amount, due date, status, customer, classification and payment method. Overdue is derived: `pending` with `due_date` before today."
    ReceivableChargeRequest:
      type: object
      properties:
        kind:
          type: string
          enum:
            - bank_billet
            - pix
          description: "Which rail to issue on. They are not the same charge with a flag: a bank slip carries the payer inline and needs their **full address**, while a Pix needs only name and document — but a Pix charging account must exist on the gateway account, and a bank slip needs a registered agreement."
          example: pix
      required:
        - kind
      additionalProperties: false
      description: "Request body to issue a charge for a receivable through the connected bank: the `kind` of charge."
    ReceivableClassificationCenterAllocationV1:
      type: object
      properties:
        classification_center:
          type: object
          properties:
            id:
              type: string
              format: uuid
            name:
              type:
                - string
                - "null"
          required:
            - id
            - name
          description: The classification center. `name` is `null` if the join is unavailable.
        percentage:
          type:
            - number
            - "null"
          description: Share of the total, 0–100 with up to 2 decimals. `null` when created amount-only.
        amount:
          type:
            - number
            - "null"
          description: Allocated amount in BRL. `null` when created percentage-only.
      required:
        - classification_center
        - percentage
        - amount
      description: "How a receivable is split across classification centers: one center and its share."
    ReceivableCustomClassificationV1:
      type: object
      properties:
        custom_classifier:
          type: object
          properties:
            id:
              type: string
              format: uuid
            name:
              type:
                - string
                - "null"
          required:
            - id
            - name
          description: The dimension. `name` is `null` if the join is unavailable.
        custom_classifier_value:
          type:
            - object
            - "null"
          properties:
            id:
              type: string
              format: uuid
            name:
              type:
                - string
                - "null"
          required:
            - id
            - name
          description: Assigned list value (id + label). `null` for scalar dimensions (see `raw_value`) or when the join is unavailable.
        raw_value:
          type:
            - string
            - "null"
          description: Free value for scalar dimensions; `null` for list dimensions.
        percentage:
          type:
            - number
            - "null"
          description: Share of the total, 0–100 with up to 2 decimals. `null` for native single-select assignments.
        amount:
          type:
            - number
            - "null"
          description: Allocated amount in BRL. `null` for native single-select assignments.
      required:
        - custom_classifier
        - custom_classifier_value
        - raw_value
        - percentage
        - amount
      description: A custom classifier value applied to a receivable.
    ReceivableTaxChargeV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
        tax:
          type: object
          properties:
            id:
              type: string
              format: uuid
            name:
              type:
                - string
                - "null"
          required:
            - id
            - name
          description: The tax. `name` is `null` if the join is unavailable.
        amount:
          type: number
        withholding:
          type: boolean
      required:
        - id
        - tax
        - amount
        - withholding
      description: A tax withheld or charged on a receivable.
    ReceivableReconciliationV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
        source:
          type: string
          description: '`"manual"` (user-driven) or `"fintera"` (imported).'
        transaction_ids:
          type: array
          items:
            type: string
            format: uuid
        billing_ids:
          type: array
          items:
            type: string
            format: uuid
      required:
        - id
        - source
        - transaction_ids
        - billing_ids
      description: "The reconciliation a receivable is part of: its source and the transactions and billings it links."
    ReceivableDetailV1:
      allOf:
        - $ref: "#/components/schemas/ReceivableV1"
        - type: object
          properties:
            charge:
              type:
                - object
                - "null"
              properties:
                uid:
                  type:
                    - string
                    - "null"
                kind:
                  type:
                    - string
                    - "null"
                  enum:
                    - bank_billet
                    - pix
                    - null
                  description: Which rail the charge was issued on. `null` when never charged.
                status:
                  type:
                    - string
                    - "null"
                  description: "**Commercial** axis (`opened`, `paid`, `overdue`, `canceled`, `generation_failed`, …). Free string — Kobana owns the vocabulary and may add values. ⚠️ Do not branch on this alone: an `opened` charge whose registration has not confirmed is not payable. Use `state`."
                registration_status:
                  type:
                    - string
                    - "null"
                  description: "**Registration** axis — the state at the financial institution (`pending`, `confirmed`, `skipped`, `rejected`, `failed`). Free string. This is the axis that says whether the charge can be handed to the payer."
                state:
                  type:
                    - string
                    - "null"
                  enum:
                    - registering
                    - registered
                    - overdue
                    - paid
                    - registration_rejected
                    - registration_failed
                    - generation_failed
                    - canceled
                    - null
                  description: "Computed by Finance (not a Kobana field, and not persisted): the two axes above collapsed into the one state to branch on. A terminal commercial state wins; below it the registration decides between `registering` and `registered`. `registered` is the first state in which the charge can be delivered to the payer — before it, a bank slip does not exist at the bank and a Pix has no QR code."
                status_updated_at:
                  type:
                    - string
                    - "null"
                  format: date-time
                  description: When the charge mirror was last written (ISO 8601).
                id:
                  type:
                    - integer
                    - "null"
                  description: The Kobana numeric id. Only a bank slip carries one — it is the key the boleto is re-read by, while a Pix charge is read by `uid`.
                url:
                  type:
                    - string
                    - "null"
                  description: Hosted page for the charge, safe to hand to the payer. `null` until the charge exists.
                barcode:
                  type:
                    - string
                    - "null"
                  description: Bank slip barcode (the scannable digits). `null` on a Pix charge, and until registration confirms.
                digitable_line:
                  type:
                    - string
                    - "null"
                  description: Bank slip digitable line — what a person types into their banking app. `null` on a Pix charge.
                pix_qrcode:
                  type:
                    - string
                    - "null"
                  description: "Pix copia-e-cola (EMV payload). `null` on a bank slip. ⚠️ Also `null` on a freshly issued Pix charge: it only arrives once the institution confirms the registration, so poll until `state` reaches `registered` before sending it to the payer."
              required:
                - uid
                - kind
                - status
                - registration_status
                - state
                - status_updated_at
                - id
                - url
                - barcode
                - digitable_line
                - pix_qrcode
              description: Charge mirror. `null` when never charged.
            classification_center_allocations:
              type: array
              items:
                $ref: "#/components/schemas/ReceivableClassificationCenterAllocationV1"
              description: Rateio rows (classification-center allocations). Empty when no split.
            custom_classifications:
              type: array
              items:
                $ref: "#/components/schemas/ReceivableCustomClassificationV1"
              description: Custom-classifier assignments (one per dimension for native single-select). Empty when none.
            tax_charges:
              type: array
              items:
                $ref: "#/components/schemas/ReceivableTaxChargeV1"
              description: Withholding/tax charges attached to the receivable. Empty when none.
            reconciliation:
              allOf:
                - $ref: "#/components/schemas/ReceivableReconciliationV1"
                - type:
                    - object
                    - "null"
                  description: The reconciliation group this receivable belongs to, or `null` when not reconciled.
          required:
            - classification_center_allocations
            - custom_classifications
            - tax_charges
            - reconciliation
      description: "A receivable as `GET /receivables/{id}` returns it: everything in `ReceivableV1` plus the classification-center split, custom classifications, taxes, reconciliation and the charge issued for it."
    ReceivableListMeta:
      type: object
      properties:
        next_cursor:
          type:
            - string
            - "null"
          description: Opaque cursor. Pass back as `?cursor=…` to fetch the next page. `null` when there is no next page.
      required:
        - next_cursor
      description: "Pagination metadata of a receivables listing: `next_cursor` to pass as `cursor` for the next page (null on the last)."
    ReceivableListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/ReceivableV1"
        meta:
          $ref: "#/components/schemas/ReceivableListMeta"
      required:
        - data
        - meta
      description: "One page of receivables: the records in `data` and the pagination in `meta`."
    ReceivableClassificationCenterSplitInput:
      type: object
      properties:
        classification_center:
          type: object
          properties:
            id:
              type: string
              format: uuid
          required:
            - id
          additionalProperties: false
        percentage:
          type: number
          description: Share of the total (0–100). Provide `percentage` and/or `amount`.
        amount:
          type: number
          description: Allocated amount in BRL, in BRL (`1920.0`). Provide `percentage` and/or `amount`. A JSON number is still accepted for backward compatibility; the string is canonical and is what the reads return.
      required:
        - classification_center
      description: One line of the classification-center split of a receivable, as sent on create or update.
    ReceivableCustomClassificationInput:
      type: object
      properties:
        custom_classifier:
          type: object
          properties:
            id:
              type: string
              format: uuid
          required:
            - id
          additionalProperties: false
        custom_classifier_value:
          type: object
          properties:
            id:
              type: string
              format: uuid
            name:
              type: string
              minLength: 1
              maxLength: 255
          additionalProperties: false
          description: "Assigned value: `{id}` for an existing value, or `{name}` to find-or-create it under the dimension."
        raw_value:
          type: string
          maxLength: 2000
          description: Free value for a scalar dimension. Use instead of a value id/name.
        percentage:
          type: number
          description: Optional rateio share (0–100). Omit for native single-select (stored as null).
        amount:
          type: number
          description: Optional rateio amount in BRL, in BRL. Omit for native single-select.
      required:
        - custom_classifier
      description: A custom classifier value to apply to a receivable, as sent on create or update.
    ReceivableTaxChargeInput:
      type: object
      properties:
        tax:
          type: object
          properties:
            id:
              type: string
              format: uuid
          required:
            - id
          additionalProperties: false
        amount:
          type: number
          description: Charge amount in BRL, in BRL (`"30.00"`).
        withholding:
          type: boolean
          description: Defaults to `true` (withheld).
      required:
        - tax
        - amount
      description: A tax to withhold or charge on a receivable, as sent on create or update.
    ReceivableCreateRequest:
      type: object
      properties:
        company_id:
          type: string
          format: uuid
          description: Owning company UUID. Must belong to the tenant.
        description:
          type: string
          minLength: 1
          maxLength: 500
        amount:
          type: number
          description: A number in BRL (up to 13 integer digits, 2 decimals). Always positive.
          example: 1500
        due_date:
          type: string
          format: date-time
        competence_date:
          type: string
          format: date-time
          description: Accounting period (ISO 8601). Defaults to `due_date` when omitted.
        type:
          type: string
          enum:
            - normal
            - recurring
            - installment
          description: Defaults to `normal`. `recurring` requires `recurrence_period` and stores the series ahead up to the workspace's provisioning horizon (default 12 months) as rows sharing `group_id`; `installment` requires `total_installments` (2–120). A title that belongs to a series keeps its `type`; a standalone `normal` title may become `recurring`.
        person:
          type:
            - object
            - "null"
          properties:
            id:
              type: string
              format: uuid
            name:
              type: string
              minLength: 1
              maxLength: 255
          additionalProperties: false
          description: "`{id}` for an existing person, or `{name}` to find-or-create one with `customer` polarity, scoped to (workspace, company). Never both."
        category:
          type:
            - object
            - "null"
          properties:
            id:
              type: string
              format: uuid
            name:
              type: string
              minLength: 1
              maxLength: 255
          additionalProperties: false
          description: "`{id}` for an existing category, or `{name}` to find-or-create one with `credit` polarity. Never both."
        classification_center:
          type:
            - object
            - "null"
          properties:
            id:
              type: string
              format: uuid
            name:
              type: string
              minLength: 1
              maxLength: 255
          additionalProperties: false
          description: "`{id}` for an existing classification center, or `{name}` to find-or-create one with `revenue` polarity — matches a center typed `revenue` or `both`; creates as `revenue` when no match. Never both."
        payment_method_kind:
          type:
            - string
            - "null"
          enum:
            - bank_billet
            - ted
            - pix
            - credit_card
            - debit_card
            - cash
            - check
            - direct_debit
            - other
            - none
            - null
        financial_account_id:
          type:
            - string
            - "null"
          format: uuid
          description: Optional pre-set receipt account. Must belong to the tenant and match `company_id`.
        expected_account_id:
          type:
            - string
            - "null"
          format: uuid
          description: Projected receipt account (forecast). Independent of `financial_account_id`.
        document:
          type:
            - string
            - "null"
          maxLength: 100
          description: Free-form reference of the source document (e.g. `NF-12345`, `BOL-001`); what the `search` / `documentContains` filters match. Distinct from `document_number`.
        document_date:
          type:
            - string
            - "null"
          format: date-time
        document_number:
          type:
            - string
            - "null"
          maxLength: 100
          description: Structured document number — the bare identifier (e.g. `12345`). Mirrors the "Nº de documento" field in the app UI; the canonical field for manually-entered document numbers. Distinct from the free-form `document`.
        notes:
          type:
            - string
            - "null"
          maxLength: 2000
        ir_relevant:
          type:
            - boolean
            - "null"
        recurrence_period:
          type: string
          maxLength: 50
          description: Required for `type=recurring` and rejected for any other type (`weekly` | `biweekly` | `monthly` | `quarterly` | `yearly`).
        total_installments:
          type: integer
          minimum: 2
          maximum: 120
          description: Required for `type=installment` and rejected for any other type (2–120). A title that belongs to a series keeps its `type` and `total_installments`.
        classification_centers:
          type: array
          items:
            $ref: "#/components/schemas/ReceivableClassificationCenterSplitInput"
          description: Rateio. Replace-all set; Σ validated against `amount` (0.01 tolerance).
        custom_classifications:
          type: array
          items:
            $ref: "#/components/schemas/ReceivableCustomClassificationInput"
          description: Custom-classifier assignments. Replace-all set; one value per dimension for native single-select. Replicated onto every installment.
        tax_charges:
          type: array
          items:
            $ref: "#/components/schemas/ReceivableTaxChargeInput"
          description: Withholding/tax charges. Replace-all set.
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: "Metadados do usuário: objeto JSON livre. Envie `null` para limpar. Máx. 64 KB serializado, 16 níveis."
      required:
        - company_id
        - description
        - amount
        - due_date
      additionalProperties: false
      description: Request body to create a receivable.
    ReceivableUpdateRequest:
      type: object
      properties:
        description:
          type: string
          minLength: 1
          maxLength: 500
        amount:
          type: number
          description: A number in BRL (up to 13 integer digits, 2 decimals). Always positive.
          example: 1500
        due_date:
          type: string
          format: date-time
        competence_date:
          type: string
          format: date-time
          description: Accounting period (ISO 8601). Defaults to `due_date` when omitted.
        type:
          type: string
          enum:
            - normal
            - recurring
            - installment
          description: Defaults to `normal`. `recurring` requires `recurrence_period` and stores the series ahead up to the workspace's provisioning horizon (default 12 months) as rows sharing `group_id`; `installment` requires `total_installments` (2–120). A title that belongs to a series keeps its `type`; a standalone `normal` title may become `recurring`.
        person:
          type:
            - object
            - "null"
          properties:
            id:
              type: string
              format: uuid
            name:
              type: string
              minLength: 1
              maxLength: 255
          additionalProperties: false
          description: "`{id}` for an existing person, or `{name}` to find-or-create one with `customer` polarity, scoped to (workspace, company). Never both."
        category:
          type:
            - object
            - "null"
          properties:
            id:
              type: string
              format: uuid
            name:
              type: string
              minLength: 1
              maxLength: 255
          additionalProperties: false
          description: "`{id}` for an existing category, or `{name}` to find-or-create one with `credit` polarity. Never both."
        classification_center:
          type:
            - object
            - "null"
          properties:
            id:
              type: string
              format: uuid
            name:
              type: string
              minLength: 1
              maxLength: 255
          additionalProperties: false
          description: "`{id}` for an existing classification center, or `{name}` to find-or-create one with `revenue` polarity — matches a center typed `revenue` or `both`; creates as `revenue` when no match. Never both."
        payment_method_kind:
          type:
            - string
            - "null"
          enum:
            - bank_billet
            - ted
            - pix
            - credit_card
            - debit_card
            - cash
            - check
            - direct_debit
            - other
            - none
            - null
        financial_account_id:
          type:
            - string
            - "null"
          format: uuid
          description: Optional pre-set receipt account. Must belong to the tenant and match `company_id`.
        expected_account_id:
          type:
            - string
            - "null"
          format: uuid
          description: Projected receipt account (forecast). Independent of `financial_account_id`.
        document:
          type:
            - string
            - "null"
          maxLength: 100
          description: Free-form reference of the source document (e.g. `NF-12345`, `BOL-001`); what the `search` / `documentContains` filters match. Distinct from `document_number`.
        document_date:
          type:
            - string
            - "null"
          format: date-time
        document_number:
          type:
            - string
            - "null"
          maxLength: 100
          description: Structured document number — the bare identifier (e.g. `12345`). Mirrors the "Nº de documento" field in the app UI; the canonical field for manually-entered document numbers. Distinct from the free-form `document`.
        notes:
          type:
            - string
            - "null"
          maxLength: 2000
        ir_relevant:
          type:
            - boolean
            - "null"
        recurrence_period:
          type: string
          maxLength: 50
          description: Required for `type=recurring` and rejected for any other type (`weekly` | `biweekly` | `monthly` | `quarterly` | `yearly`).
        total_installments:
          type: integer
          minimum: 2
          maximum: 120
          description: Required for `type=installment` and rejected for any other type (2–120). A title that belongs to a series keeps its `type` and `total_installments`.
        classification_centers:
          type: array
          items:
            $ref: "#/components/schemas/ReceivableClassificationCenterSplitInput"
          description: Rateio. Replace-all set; Σ validated against `amount` (0.01 tolerance).
        custom_classifications:
          type: array
          items:
            $ref: "#/components/schemas/ReceivableCustomClassificationInput"
          description: Custom-classifier assignments. Replace-all set; one value per dimension for native single-select. Replicated onto every installment.
        tax_charges:
          type: array
          items:
            $ref: "#/components/schemas/ReceivableTaxChargeInput"
          description: Withholding/tax charges. Replace-all set.
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: "Metadados do usuário: objeto JSON livre. Envie `null` para limpar. Máx. 64 KB serializado, 16 níveis."
        status:
          type: string
          enum:
            - pending
            - received
            - cancelled
          description: Settle (`received`), revert (`pending`) or cancel (`cancelled`). `draft` is never accepted.
        payment_date:
          type:
            - string
            - "null"
          format: date-time
          description: Receipt date (ISO 8601). Required when moving to `status=received`.
        interest_amount:
          type:
            - number
            - "null"
          description: Late-payment interest collected at receive time (BRL), in BRL.
        discount_amount:
          type:
            - number
            - "null"
          description: Discount granted at receive time (BRL), in BRL.
        ticket_amount:
          type:
            - number
            - "null"
          description: '"Valor liquidado" — the actual amount that hit the bank (BRL), in BRL.'
        custom_metadata_mode:
          type: string
          enum:
            - replace
            - merge
          description: "Como o `custom_metadata` enviado é reconciliado com o valor armazenado: `replace` (padrão) substitui o objeto inteiro, `merge` faz deep merge sobre o valor atual. Aceito apenas na atualização."
      additionalProperties: false
      description: "Request body to update a receivable: send only the fields to change."
    AttachmentV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
          example: 8c8b4c4f-2e0e-4f6e-9b8a-2a1c2b3d4e5f
        file_name:
          type: string
          description: Original file name at upload time.
          example: nota-fiscal-1234.pdf
        title:
          type:
            - string
            - "null"
          description: Optional user-supplied label that overrides `file_name` in UIs. `null` when unset.
        mime_type:
          type: string
          description: MIME type as captured at upload time.
          example: application/pdf
        file_size:
          type: integer
          minimum: 0
          description: Size in bytes. Capped at the upload-time `BILLING_IMPORT_MAX_FILE_BYTES` limit (10 MB by default).
          example: 184320
        uploaded_by:
          type: string
          description: Who uploaded the file. Free-form (no FK to a users table) — the in-app uploader records the user email; uploads via the public API record `api:<sub>` (the token's `sub`/account public id).
        uploaded_at:
          type: string
          format: date-time
        download_url:
          type: string
          description: "Absolute URL to fetch the file bytes. Same Bearer JWT required (scope `finance.attachments`). Streams with `Content-Disposition: inline` for PDF/images, `attachment` otherwise."
          example: https://finance.kobana.example/api/v1/attachments/8c8b4c4f-2e0e-4f6e-9b8a-2a1c2b3d4e5f/download
        linked_payables_count:
          type: integer
          minimum: 0
          description: How many payables reference this attachment.
        linked_receivables_count:
          type: integer
          minimum: 0
          description: How many receivables reference this attachment.
        linked_transactions_count:
          type: integer
          minimum: 0
          description: How many transactions reference this attachment.
        metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadados do sistema, gerenciados pela Kobana. Somente leitura.
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: "Metadados do usuário: objeto JSON livre."
        created_via:
          type:
            - string
            - "null"
          enum:
            - import
            - dashboard
            - api
            - cli
            - mcp
            - other
            - null
          description: "Channel that created the record: `dashboard`, `import`, `api`, `mcp`, `cli` or `other`. On API calls it is the `X-Request-Source` header. `null` when the change came from the system (scheduled jobs) or predates this tracking."
          example: api
        created_via_other:
          type:
            - string
            - "null"
          description: App that created the record when the channel is `other` (`X-Request-Source-Other`).
          example: null
        created_by_email:
          type:
            - string
            - "null"
          description: E-mail of the person who created the record. `null` for system changes.
          example: maria@empresa.com.br
        updated_via:
          type:
            - string
            - "null"
          enum:
            - import
            - dashboard
            - api
            - cli
            - mcp
            - other
            - null
          description: "Channel that last changed the record: `dashboard`, `import`, `api`, `mcp`, `cli` or `other`. On API calls it is the `X-Request-Source` header. `null` when the change came from the system (scheduled jobs) or predates this tracking."
          example: api
        updated_via_other:
          type:
            - string
            - "null"
          description: App that last changed the record when the channel is `other` (`X-Request-Source-Other`).
          example: null
        updated_by_email:
          type:
            - string
            - "null"
          description: E-mail of the person who last changed the record. `null` for system changes.
          example: maria@empresa.com.br
      required:
        - id
        - file_name
        - title
        - mime_type
        - file_size
        - uploaded_by
        - uploaded_at
        - download_url
        - linked_payables_count
        - linked_receivables_count
        - linked_transactions_count
        - metadata
        - custom_metadata
        - created_via
        - created_via_other
        - created_by_email
        - updated_via
        - updated_via_other
        - updated_by_email
      description: "An attachment: a stored file (receipt, boleto, invoice XML…) with its metadata and the records it is linked to."
    AttachmentListMeta:
      type: object
      properties:
        next_cursor:
          type:
            - string
            - "null"
          description: Opaque cursor. Pass back as `?cursor=…` to fetch the next page. `null` when there is no next page.
      required:
        - next_cursor
      description: "Pagination metadata of a attachments listing: `next_cursor` to pass as `cursor` for the next page (null on the last)."
    AttachmentListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/AttachmentV1"
        meta:
          $ref: "#/components/schemas/AttachmentListMeta"
      required:
        - data
        - meta
      description: "One page of attachments: the records in `data` and the pagination in `meta`."
    AttachmentUploadRequest:
      type: object
      properties:
        file:
          type: string
          format: binary
          description: "The file bytes (multipart field `file`). Allowed types: pdf, png, jpg/jpeg, xml, ofx, csv, xlsx. Max 10 MB. For PDF/PNG/JPEG the declared MIME must match the payload's magic bytes (mismatch → 422)."
        title:
          type: string
          description: Optional label that overrides `file_name` in UIs (multipart field `title`).
          example: Nota fiscal de junho
        custom_metadata:
          type: string
          description: Metadados do usuário, como texto JSON (multipart field `custom_metadata`) — multipart carries no types, so the object is sent serialised. Máx. 64 KB, 16 níveis. JSON inválido ou um valor que não seja objeto → 422.
          example: '{"origem":"scanner-recepcao"}'
      required:
        - file
      additionalProperties: false
      description: "Request body to upload an attachment: the file in base64, its name and MIME type."
    AttachmentLinkRequest:
      type: object
      properties:
        type:
          type: string
          enum:
            - payable
            - receivable
            - transaction
          description: Kind of parent record to (un)link the attachment to.
          example: payable
        id:
          type: string
          format: uuid
          description: Id of the parent payable / receivable / transaction within the same workspace.
          example: 8c8b4c4f-2e0e-4f6e-9b8a-2a1c2b3d4e5f
      required:
        - type
        - id
      additionalProperties: false
      description: Request body to link an attachment to a payable, receivable or financial transaction.
    ReceiptV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
          example: 6f2c1a3e-1b4d-4f8a-9c2e-7d5b8a1c3e9f
        company_id:
          type:
            - string
            - "null"
          format: uuid
          description: Owning company. `null` on an upload that was not attributed to one.
        payable_id:
          type:
            - string
            - "null"
          format: uuid
          description: The bill this receipt proves. Never `null` on a captured receipt — through the gateway the bill is always known. `null` marks an orphan upload, which is what the "link to a bill" flow exists for.
        payable_status:
          type:
            - string
            - "null"
          enum:
            - draft
            - pending
            - paid
            - cancelled
            - null
          description: 'Status of the bill this receipt points at, so "I have the proof" and "the bill was settled" can be told apart without a second call. **A receipt does not settle anything** — through the gateway the settlement happens on confirmation, but an uploaded one leaves the bill exactly as it was, which is why `paid_at` and a `pending` here coexist normally. `null` when no bill is linked. Not filterable — it is a join, not a column. ⚠️ `draft` appears here and never on `PayableV1.status`: a draft is reachable by linking one by hand.'
        financial_account_id:
          type:
            - string
            - "null"
          format: uuid
          description: Account the money left from, when known.
        source:
          type: string
          enum:
            - kobana
            - upload
          description: "`kobana` — captured automatically when the dispatched payment was confirmed. `upload` — a file sent by a person for a payment made outside the system."
        status:
          type: string
          enum:
            - pending
            - available
            - failed
          description: "`pending` — the document is still being fetched (normal for a few minutes after confirmation). `available` — reachable through `download_url` or `document_url`. `failed` — the fetch gave up; see `error_message`."
        paid_at:
          type: string
          format: date-time
          description: "Date the payment happened. A date column: naive, midnight UTC."
        amount:
          type: number
          description: Amount paid, in BRL.
          example: 1500
        payee_name:
          type:
            - string
            - "null"
          description: Who received the money. On a captured receipt this is the bill's beneficiary when it has one, falling back to its supplier — a boleto or utility bill carries no beneficiary of its own.
        payee_document:
          type:
            - string
            - "null"
          description: Payee CPF/CNPJ, unmasked when known.
        payment_method_kind:
          type:
            - string
            - "null"
          enum:
            - bank_billet
            - utility
            - tribute
            - ted
            - pix
            - credit_card
            - debit_card
            - cash
            - check
            - direct_debit
            - other
            - none
            - null
          description: How the payment settled. The payable set — receipts only exist on the paying side.
        external_uid:
          type:
            - string
            - "null"
          description: The gateway payment `uid` this receipt was captured from — the same value the payable carries as `kobana_payment_uid`, for correlating against your own gateway data. `null` on an upload.
        attachment_id:
          type:
            - string
            - "null"
          format: uuid
          description: The stored document in the workspace attachment library. `null` while the capture is pending, and also on an `available` row whose format could not be stored — that row is served by `document_url`.
        download_url:
          type:
            - string
            - "null"
          description: Absolute URL for the stored bytes. `null` when there is no `attachment_id`. ⚠️ It points at the attachments resource, so fetching it needs a token carrying **`finance.attachments`** — `finance.receipts` alone lists receipts but does not download them.
          example: https://finance.kobana.example/api/v1/attachments/8c8b4c4f-2e0e-4f6e-9b8a-2a1c2b3d4e5f/download
        document_url:
          type:
            - string
            - "null"
          description: The document at the origin. Populated on a captured receipt, and the only way to reach one whose format the attachment whitelist refuses. Points at a third-party host, not at this API — no Bearer of ours applies.
        error_message:
          type:
            - string
            - "null"
          description: Why the capture gave up. Only set while `status` is `failed`.
        received_at:
          type:
            - string
            - "null"
          format: date-time
          description: When the confirmation that triggered the capture was observed. `null` on an upload.
        metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadados do sistema, gerenciados pela Kobana. Somente leitura.
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: "Metadados do usuário: objeto JSON livre."
        created_via:
          type:
            - string
            - "null"
          enum:
            - import
            - dashboard
            - api
            - cli
            - mcp
            - other
            - null
          description: "Channel that created the record: `dashboard`, `import`, `api`, `mcp`, `cli` or `other`. On API calls it is the `X-Request-Source` header. `null` when the change came from the system (scheduled jobs) or predates this tracking."
          example: api
        created_via_other:
          type:
            - string
            - "null"
          description: App that created the record when the channel is `other` (`X-Request-Source-Other`).
          example: null
        created_by_email:
          type:
            - string
            - "null"
          description: E-mail of the person who created the record. `null` for system changes.
          example: maria@empresa.com.br
        updated_via:
          type:
            - string
            - "null"
          enum:
            - import
            - dashboard
            - api
            - cli
            - mcp
            - other
            - null
          description: "Channel that last changed the record: `dashboard`, `import`, `api`, `mcp`, `cli` or `other`. On API calls it is the `X-Request-Source` header. `null` when the change came from the system (scheduled jobs) or predates this tracking."
          example: api
        updated_via_other:
          type:
            - string
            - "null"
          description: App that last changed the record when the channel is `other` (`X-Request-Source-Other`).
          example: null
        updated_by_email:
          type:
            - string
            - "null"
          description: E-mail of the person who last changed the record. `null` for system changes.
          example: maria@empresa.com.br
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
          description: Last-write timestamp (ISO 8601). Bumped when the capture lands or its status changes.
      required:
        - id
        - company_id
        - payable_id
        - payable_status
        - financial_account_id
        - source
        - status
        - paid_at
        - amount
        - payee_name
        - payee_document
        - payment_method_kind
        - external_uid
        - attachment_id
        - download_url
        - document_url
        - error_message
        - received_at
        - metadata
        - custom_metadata
        - created_via
        - created_via_other
        - created_by_email
        - updated_via
        - updated_via_other
        - updated_by_email
        - created_at
        - updated_at
      description: "A payment receipt: proof of a payment made, with the payable it settles and a link to download the file."
    ReceiptListMeta:
      type: object
      properties:
        next_cursor:
          type:
            - string
            - "null"
          description: Opaque cursor. Pass back as `?cursor=…` to fetch the next page. `null` when there is no next page.
      required:
        - next_cursor
      description: "Pagination metadata of a payment receipts listing: `next_cursor` to pass as `cursor` for the next page (null on the last)."
    ReceiptListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/ReceiptV1"
        meta:
          $ref: "#/components/schemas/ReceiptListMeta"
      required:
        - data
        - meta
      description: "One page of payment receipts: the records in `data` and the pagination in `meta`."
    BankV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
          example: b3d9a5f8-2c1e-4a90-9f23-7e6b1c4d8a02
        code:
          type: string
          description: COMPE/BACEN clearing code. Unique across the catalog (e.g. `001`, `237`, `341`).
          example: "341"
        name:
          type: string
          description: Official institution name.
          example: Itaú Unibanco S.A.
      required:
        - id
        - code
        - name
      description: "A bank from the Brazilian bank directory: its code and name."
    BankListMeta:
      type: object
      properties:
        next_cursor:
          type:
            - string
            - "null"
          description: Opaque cursor. Pass back as `?cursor=…` to fetch the next page. `null` when there is no next page.
      required:
        - next_cursor
      description: "Pagination metadata of a banks listing: `next_cursor` to pass as `cursor` for the next page (null on the last)."
    BankListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/BankV1"
        meta:
          $ref: "#/components/schemas/BankListMeta"
      required:
        - data
        - meta
      description: "One page of banks: the records in `data` and the pagination in `meta`."
    TransferV1:
      type: object
      properties:
        transfer_id:
          type: string
          format: uuid
          description: Stable id of the linked pair. Read a transfer via `GET /transfers/{transfer_id}`.
          example: 9c2f1a44-7b3e-4e01-9a5c-2d6b8e1f0a33
        from_account_id:
          type: string
          format: uuid
          description: Source (debited) financial account.
        to_account_id:
          type: string
          format: uuid
          description: Destination (credited) financial account.
        from_transaction_id:
          type: string
          format: uuid
          description: The `debit` leg's transaction id (money out of `from_account_id`).
        to_transaction_id:
          type: string
          format: uuid
          description: The `credit` leg's transaction id (money into `to_account_id`).
        amount:
          type: number
          description: Positive number in BRL. Same magnitude on both legs.
          example: 1500
        occurred_at:
          type: string
          format: date-time
          description: When the movement hit both accounts. Shared by both legs.
          example: 2026-07-10T00:00:00.000Z
        competence_date:
          type: string
          format: date-time
          description: Accounting period. Falls back to `occurred_at` when not supplied on create.
        description:
          type: string
          example: Aporte para conta pagamentos
        notes:
          type:
            - string
            - "null"
        is_pending:
          type: boolean
          description: When `true`, a forecast transfer — excluded from realised balance until confirmed.
        metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadados do sistema, gerenciados pela Kobana. Somente leitura.
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: "Metadados do usuário: objeto JSON livre. A transfer has no table of its own — it is a linked pair of transactions — so the value is written to BOTH legs and read from the debit one."
      required:
        - transfer_id
        - from_account_id
        - to_account_id
        - from_transaction_id
        - to_transaction_id
        - amount
        - occurred_at
        - competence_date
        - description
        - notes
        - is_pending
        - metadata
        - custom_metadata
      description: "A transfer between two financial accounts of the workspace: the debit and credit transactions it created."
    TransferCreateRequest:
      type: object
      properties:
        from_account_id:
          type: string
          format: uuid
          description: Source account UUID (debited). Must belong to the tenant.
        to_account_id:
          type: string
          format: uuid
          description: Destination account UUID (credited). Must belong to the tenant and differ from `from_account_id`.
        amount:
          type: number
          description: Positive number in BRL (up to 13 integer digits, 2 decimals).
          example: 1500
        occurred_at:
          type: string
          format: date-time
          description: When the movement hits both accounts (ISO 8601).
        competence_date:
          type:
            - string
            - "null"
          format: date-time
          description: Accounting period this transfer belongs to. Defaults to `occurred_at` when omitted.
        description:
          type: string
          minLength: 1
          maxLength: 500
          example: Aporte para conta pagamentos
        notes:
          type:
            - string
            - "null"
          maxLength: 2000
        is_pending:
          type: boolean
          description: Defaults to `false`. `true` marks a forecast transfer.
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: "Metadados do usuário: objeto JSON livre, gravado nas duas pernas. Envie `null` para limpar. Máx. 64 KB serializado, 16 níveis."
      required:
        - from_account_id
        - to_account_id
        - amount
        - occurred_at
        - description
      additionalProperties: false
      description: Request body to create a transfer between financial accounts.
    PayableAggregatesV1:
      type: object
      properties:
        count:
          type: integer
          description: Count of non-draft payables in scope. Includes cancelled rows — which the `pending`/`paid`/`total` money buckets exclude — so `count` may exceed the number of rows behind `total`.
        total:
          type: number
          description: Sum of `pending`+`paid` amounts (cancelled excluded).
          example: 12500
        pending:
          type: number
          description: Sum of amounts still pending.
          example: 8000
        paid:
          type: number
          description: Sum of amounts already paid.
          example: 4500
        overdue_count:
          type: integer
          description: Pending payables in the window whose `due_date` is already past. 0 for a fully-future window.
      required:
        - count
        - total
        - pending
        - paid
        - overdue_count
      description: "Totals of payables for a period: pending, paid, overdue and their counts."
    ReceivableAggregatesV1:
      type: object
      properties:
        count:
          type: integer
          description: Count of non-draft receivables in scope. Includes cancelled rows — which the `pending`/`received`/`total` money buckets exclude — so `count` may exceed the number of rows behind `total`.
        total:
          type: number
          description: Sum of `pending`+`received` amounts (cancelled excluded).
          example: 9000
        pending:
          type: number
          description: Sum of amounts still pending.
          example: 6000
        received:
          type: number
          description: Sum of amounts already received.
          example: 3000
        overdue_count:
          type: integer
          description: Pending receivables in the window whose `due_date` is already past. 0 for a fully-future window.
      required:
        - count
        - total
        - pending
        - received
        - overdue_count
      description: "Totals of receivables for a period: pending, received, overdue and their counts."
    TransactionAggregatesV1:
      type: object
      properties:
        count:
          type: integer
          description: Number of realised transactions in the window (forecasts excluded).
        credit:
          type: number
          description: Sum of credit (money-in) amounts.
          example: 20000
        debit:
          type: number
          description: Sum of debit (money-out) amounts.
          example: 12000
        net:
          type: number
          description: "`credit` − `debit`. May be negative."
          example: 8000
      required:
        - count
        - credit
        - debit
        - net
      description: "Totals of realised transactions for a period: credits, debits and net. Forecasts are excluded."
    CashFlowBucketV1:
      type: object
      properties:
        bucket:
          type: string
          description: Canonical bucket key. Format depends on `granularity` (e.g. `2026-07` for month, `2026-07-13` for day).
          example: 2026-07
        income:
          type: number
          description: Total money-in in the bucket (realised + projected).
          example: 12000
        expenses:
          type: number
          description: Total money-out in the bucket (realised + projected).
          example: 8000
        net:
          type: number
          description: "`income` − `expenses`. May be negative."
          example: 4000
        projected_income:
          type: number
          description: The projected-only slice of `income` (pending bills + forecast). 0 on fully-realised past buckets.
        projected_expenses:
          type: number
          description: The projected-only slice of `expenses`. 0 on fully-realised past buckets.
        opening_balance:
          type: number
          description: Running balance at the start of the bucket.
        closing_balance:
          type: number
          description: Running balance at the end of the bucket (`opening_balance` + `net`).
        is_future:
          type: boolean
          description: True from the `today_bucket` onward (the projected side of the curve).
      required:
        - bucket
        - income
        - expenses
        - net
        - projected_income
        - projected_expenses
        - opening_balance
        - closing_balance
        - is_future
      description: "One period of the cash-flow projection: income, expenses and net, their projected counterparts, and the opening and closing balance."
    CashFlowV1:
      type: object
      properties:
        granularity:
          type: string
          enum:
            - day
            - week
            - month
            - year
        current_balance:
          type: number
          description: Balance as of now (`saldo atual`), the projection anchor.
          example: 15000
        today_bucket:
          type: string
          description: Canonical key of the bucket containing today — the past/future seam.
          example: 2026-07
        truncated:
          type: boolean
          description: True when the requested range exceeded the per-granularity cap and was clipped to a today-anchored window.
        retroactive_overdue:
          type: object
          properties:
            income:
              type: number
            expenses:
              type: number
          required:
            - income
            - expenses
          description: Overdue (past-due pending) flow folded into `today_bucket`. Lets a consumer flag that the today column isn't strictly today's money. Zeros when nothing is overdue.
        buckets:
          type: array
          items:
            $ref: "#/components/schemas/CashFlowBucketV1"
      required:
        - granularity
        - current_balance
        - today_bucket
        - truncated
        - retroactive_overdue
        - buckets
      description: "The cash-flow projection of a window: the current balance, today's bucket and one bucket per period — realised in the past, projected in the future."
    ReconciliationV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
        source:
          type: string
          description: 'Origin of the group: `"manual"` (user/API-driven) or `"fintera"` (imported, read-only).'
        kind:
          type: string
          enum:
            - payable
            - receivable
          description: Polarity of the group — every `billing_id` is this kind.
        billing_ids:
          type: array
          items:
            type: string
            format: uuid
          description: IDs of the payables (or receivables) settled by this reconciliation.
        transaction_ids:
          type: array
          items:
            type: string
            format: uuid
          description: IDs of the bank transactions in the group.
        metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadados do sistema, gerenciados pela Kobana. Somente leitura.
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: "Metadados do usuário: objeto JSON livre."
      required:
        - id
        - source
        - kind
        - billing_ids
        - transaction_ids
        - metadata
        - custom_metadata
      description: "A reconciliation: the link between financial transactions (`transaction_ids`) and the payables and receivables (`billing_ids`) they settle."
    ReconciliationCreateRequest:
      type: object
      properties:
        billings:
          type: array
          items:
            type: object
            properties:
              kind:
                type: string
                enum:
                  - payable
                  - receivable
              id:
                type: string
                format: uuid
            required:
              - kind
              - id
          minItems: 1
          description: Billings to reconcile. All must share one kind and one company.
        transaction_ids:
          type: array
          items:
            type: string
            format: uuid
          minItems: 1
          description: Bank transactions to reconcile the billings against.
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: "Metadados do usuário: objeto JSON livre. Envie `null` para limpar. Máx. 64 KB serializado, 16 níveis."
      required:
        - billings
        - transaction_ids
      additionalProperties: false
      description: Request body to create a reconciliation.
    AutomaticRuleV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
          example: a1f2c3d4-5e6f-4a1b-8c2d-3e4f5a6b7c8d
        company_id:
          type: string
          format: uuid
          description: Owning company. Rules are scoped to `(company, scope)`; `position` is unique within that pair.
        scope:
          type: string
          enum:
            - transactions
            - billings
          description: "Target ledger: `transactions` (Contas Financeiras) or `billings` (Payables + Receivables). Immutable after create."
        position:
          type: integer
          description: 1-based precedence within `(company, scope)`. Lower runs first (first-match-wins). Server-assigned.
          example: 1
        active:
          type: boolean
          description: Inactive rules stay in the list but are skipped by the evaluator.
        account_id:
          type:
            - string
            - "null"
          format: uuid
          description: Criterion — match rows on this financial account. Only meaningful for `scope=transactions`.
        transaction_type:
          type:
            - string
            - "null"
          enum:
            - debit
            - credit
            - null
          description: Criterion — match only debit or credit rows. `null` matches both.
        match_person_id:
          type:
            - string
            - "null"
          format: uuid
          description: Criterion — match rows already tied to this person (customer/supplier).
        description_contains:
          type:
            - string
            - "null"
          description: Criterion — case-insensitive substring on the row description. `""` is a wildcard (matches all).
        notes_contains:
          type:
            - string
            - "null"
          description: Criterion — case-insensitive substring on the row notes.
        set_description:
          type:
            - string
            - "null"
          description: Action — overwrite the description.
        set_person_id:
          type:
            - string
            - "null"
          format: uuid
          description: Action — assign this person.
        set_category_id:
          type:
            - string
            - "null"
          format: uuid
          description: Action — assign this category.
        set_classification_center_id:
          type:
            - string
            - "null"
          format: uuid
          description: Action — assign this classification center (cost/revenue center). References a `/v1/classification-centers` row.
        set_expected_account_id:
          type:
            - string
            - "null"
          format: uuid
          description: Action — set the expected settlement account. Only meaningful for `scope=billings`.
        metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadados do sistema, gerenciados pela Kobana. Somente leitura.
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: "Metadados do usuário: objeto JSON livre."
        created_via:
          type:
            - string
            - "null"
          enum:
            - import
            - dashboard
            - api
            - cli
            - mcp
            - other
            - null
          description: "Channel that created the record: `dashboard`, `import`, `api`, `mcp`, `cli` or `other`. On API calls it is the `X-Request-Source` header. `null` when the change came from the system (scheduled jobs) or predates this tracking."
          example: api
        created_via_other:
          type:
            - string
            - "null"
          description: App that created the record when the channel is `other` (`X-Request-Source-Other`).
          example: null
        created_by_email:
          type:
            - string
            - "null"
          description: E-mail of the person who created the record. `null` for system changes.
          example: maria@empresa.com.br
        updated_via:
          type:
            - string
            - "null"
          enum:
            - import
            - dashboard
            - api
            - cli
            - mcp
            - other
            - null
          description: "Channel that last changed the record: `dashboard`, `import`, `api`, `mcp`, `cli` or `other`. On API calls it is the `X-Request-Source` header. `null` when the change came from the system (scheduled jobs) or predates this tracking."
          example: api
        updated_via_other:
          type:
            - string
            - "null"
          description: App that last changed the record when the channel is `other` (`X-Request-Source-Other`).
          example: null
        updated_by_email:
          type:
            - string
            - "null"
          description: E-mail of the person who last changed the record. `null` for system changes.
          example: maria@empresa.com.br
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
        - id
        - company_id
        - scope
        - position
        - active
        - account_id
        - transaction_type
        - match_person_id
        - description_contains
        - notes_contains
        - set_description
        - set_person_id
        - set_category_id
        - set_classification_center_id
        - set_expected_account_id
        - metadata
        - custom_metadata
        - created_via
        - created_via_other
        - created_by_email
        - updated_via
        - updated_via_other
        - updated_by_email
        - created_at
        - updated_at
      description: "An automatic classification rule: conditions matched against incoming transactions and the classification applied when they match, evaluated in `position` order."
    AutomaticRuleListMeta:
      type: object
      properties:
        next_cursor:
          type:
            - string
            - "null"
          description: Opaque cursor. Pass back as `?cursor=…` to fetch the next page. `null` when there is no next page.
      required:
        - next_cursor
      description: "Pagination metadata of a automatic classification rules listing: `next_cursor` to pass as `cursor` for the next page (null on the last)."
    AutomaticRuleListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/AutomaticRuleV1"
        meta:
          $ref: "#/components/schemas/AutomaticRuleListMeta"
      required:
        - data
        - meta
      description: "One page of automatic classification rules: the records in `data` and the pagination in `meta`."
    AutomaticRuleCreateRequest:
      type: object
      properties:
        company_id:
          type: string
          format: uuid
          description: Owning company UUID. Must belong to the tenant.
        scope:
          type: string
          enum:
            - transactions
            - billings
          description: Target ledger. Immutable after create.
        active:
          type: boolean
          description: Defaults to `true`.
        account_id:
          type:
            - string
            - "null"
          format: uuid
          description: Criterion — financial account. Only valid for `scope=transactions` (422 otherwise).
        transaction_type:
          type:
            - string
            - "null"
          enum:
            - debit
            - credit
            - null
        match_person_id:
          type:
            - string
            - "null"
          format: uuid
        description_contains:
          type:
            - string
            - "null"
          maxLength: 255
          description: Case-insensitive substring on description. `""` opts into a wildcard (matches every row).
        notes_contains:
          type:
            - string
            - "null"
          maxLength: 255
        set_description:
          type:
            - string
            - "null"
          maxLength: 255
        set_person_id:
          type:
            - string
            - "null"
          format: uuid
        set_category_id:
          type:
            - string
            - "null"
          format: uuid
        set_classification_center_id:
          type:
            - string
            - "null"
          format: uuid
          description: Action — classification center. Maps to the internal cost-center column.
        set_expected_account_id:
          type:
            - string
            - "null"
          format: uuid
          description: Action — expected settlement account. Only valid for `scope=billings` (422 otherwise).
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: "Metadados do usuário: objeto JSON livre. Envie `null` para limpar. Máx. 64 KB serializado, 16 níveis."
      required:
        - company_id
        - scope
      additionalProperties: false
      description: Request body to create a automatic classification rule.
    AutomaticRuleUpdateRequest:
      type: object
      properties:
        active:
          type: boolean
        account_id:
          type:
            - string
            - "null"
          format: uuid
          description: Criterion — financial account. Only valid for `scope=transactions` (422 otherwise).
        transaction_type:
          type:
            - string
            - "null"
          enum:
            - debit
            - credit
            - null
        match_person_id:
          type:
            - string
            - "null"
          format: uuid
        description_contains:
          type:
            - string
            - "null"
          maxLength: 255
          description: Case-insensitive substring on description. `""` opts into a wildcard (matches every row).
        notes_contains:
          type:
            - string
            - "null"
          maxLength: 255
        set_description:
          type:
            - string
            - "null"
          maxLength: 255
        set_person_id:
          type:
            - string
            - "null"
          format: uuid
        set_category_id:
          type:
            - string
            - "null"
          format: uuid
        set_classification_center_id:
          type:
            - string
            - "null"
          format: uuid
          description: Action — classification center. Maps to the internal cost-center column.
        set_expected_account_id:
          type:
            - string
            - "null"
          format: uuid
          description: Action — expected settlement account. Only valid for `scope=billings` (422 otherwise).
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: "Metadados do usuário: objeto JSON livre. Envie `null` para limpar. Máx. 64 KB serializado, 16 níveis."
        custom_metadata_mode:
          type: string
          enum:
            - replace
            - merge
          description: "Como o `custom_metadata` enviado é reconciliado com o valor armazenado: `replace` (padrão) substitui o objeto inteiro, `merge` faz deep merge sobre o valor atual. Aceito apenas na atualização."
      additionalProperties: false
      description: "Request body to update a automatic classification rule: send only the fields to change."
    AutomaticRuleReorderRequest:
      type: object
      properties:
        company_id:
          type: string
          format: uuid
          description: Company whose rules are being reordered.
        scope:
          type: string
          enum:
            - transactions
            - billings
          description: Ledger whose rules are being reordered.
        ordered_ids:
          type: array
          items:
            type: string
            format: uuid
          minItems: 1
          description: The COMPLETE ordered set of rule ids for `(company_id, scope)`, first to last. Positions are rewritten 1..N to match. A partial or foreign set is rejected (422).
      required:
        - company_id
        - scope
        - ordered_ids
      additionalProperties: false
      description: "Request body to reorder automatic rules: the rule ids in the new evaluation order."
    CustomClassifierValueV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
          example: 9b1c2d3e-4f50-4a61-8b72-1c2d3e4f5061
        custom_classifier_id:
          type: string
          format: uuid
          description: The dimension this value belongs to.
        value:
          type: string
          description: The pickable label within the dimension.
          example: Sudeste
        color:
          type:
            - string
            - "null"
          description: Hex swatch for the value's bullet; `null` falls back to a positional palette color.
          example: "#2a78d6"
        active:
          type: boolean
          description: When false, the value is soft-deactivated. Existing assignments referencing it are preserved.
        metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadados do sistema, gerenciados pela Kobana. Somente leitura.
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: "Metadados do usuário: objeto JSON livre."
        created_at:
          type: string
          format: date-time
      required:
        - id
        - custom_classifier_id
        - value
        - color
        - active
        - metadata
        - custom_metadata
        - created_at
      description: One value of a custom classifier.
    CustomClassifierV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
          example: 1a2b3c4d-5e6f-4708-9a1b-2c3d4e5f6071
        company_id:
          type: string
          format: uuid
          description: Owning company. Custom classifiers are company-scoped within the workspace.
        name:
          type: string
          description: Dimension label. Unique constraint not enforced at DB level.
          example: Região
        field_type:
          type: string
          enum:
            - dropdown
            - radio
            - multi_select
            - number
            - text
            - phone
            - url
            - date
            - datetime
          description: How the dimension is filled in. List types (dropdown/radio/multi_select) carry `values`; scalars (number/text/phone/url/date/datetime) store a free value per record.
          example: dropdown
        number_format:
          type:
            - string
            - "null"
          enum:
            - formatted
            - unformatted
            - currency
            - percent
            - null
          description: Numeric presentation for a `number` field; `null` for every other field type.
          example: null
        option_style:
          type:
            - string
            - "null"
          enum:
            - default
            - dot
            - null
          description: How list options render (`dot` shows a colored bullet); `null` for non-list types.
          example: default
        active:
          type: boolean
          description: When false, the dimension is soft-deactivated. Existing assignments are preserved.
        metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadados do sistema, gerenciados pela Kobana. Somente leitura.
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: "Metadados do usuário: objeto JSON livre."
        created_via:
          type:
            - string
            - "null"
          enum:
            - import
            - dashboard
            - api
            - cli
            - mcp
            - other
            - null
          description: "Channel that created the record: `dashboard`, `import`, `api`, `mcp`, `cli` or `other`. On API calls it is the `X-Request-Source` header. `null` when the change came from the system (scheduled jobs) or predates this tracking."
          example: api
        created_via_other:
          type:
            - string
            - "null"
          description: App that created the record when the channel is `other` (`X-Request-Source-Other`).
          example: null
        created_by_email:
          type:
            - string
            - "null"
          description: E-mail of the person who created the record. `null` for system changes.
          example: maria@empresa.com.br
        updated_via:
          type:
            - string
            - "null"
          enum:
            - import
            - dashboard
            - api
            - cli
            - mcp
            - other
            - null
          description: "Channel that last changed the record: `dashboard`, `import`, `api`, `mcp`, `cli` or `other`. On API calls it is the `X-Request-Source` header. `null` when the change came from the system (scheduled jobs) or predates this tracking."
          example: api
        updated_via_other:
          type:
            - string
            - "null"
          description: App that last changed the record when the channel is `other` (`X-Request-Source-Other`).
          example: null
        updated_by_email:
          type:
            - string
            - "null"
          description: E-mail of the person who last changed the record. `null` for system changes.
          example: maria@empresa.com.br
        created_at:
          type: string
          format: date-time
        values:
          type: array
          items:
            $ref: "#/components/schemas/CustomClassifierValueV1"
          description: The dimension's values, ascending by label. Includes inactive values (see each value's `active`).
      required:
        - id
        - company_id
        - name
        - field_type
        - number_format
        - option_style
        - active
        - metadata
        - custom_metadata
        - created_via
        - created_via_other
        - created_by_email
        - updated_via
        - updated_via_other
        - updated_by_email
        - created_at
        - values
      description: "A custom classifier: a user-defined dimension (project, contract…) with its own list of values."
    CustomClassifierListMeta:
      type: object
      properties:
        next_cursor:
          type:
            - string
            - "null"
          description: Opaque cursor. Pass back as `?cursor=…` to fetch the next page. `null` when there is no next page.
      required:
        - next_cursor
      description: "Pagination metadata of a custom classifiers listing: `next_cursor` to pass as `cursor` for the next page (null on the last)."
    CustomClassifierListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/CustomClassifierV1"
        meta:
          $ref: "#/components/schemas/CustomClassifierListMeta"
      required:
        - data
        - meta
      description: "One page of custom classifiers: the records in `data` and the pagination in `meta`."
    CustomClassifierCreateRequest:
      type: object
      properties:
        company_id:
          type: string
          format: uuid
          description: Owning company UUID. Must belong to the tenant.
        name:
          type: string
          minLength: 1
          maxLength: 255
        field_type:
          type: string
          enum:
            - dropdown
            - radio
            - multi_select
            - number
            - text
            - phone
            - url
            - date
            - datetime
          description: Defaults to `dropdown`.
        number_format:
          type:
            - string
            - "null"
          enum:
            - formatted
            - unformatted
            - currency
            - percent
            - null
          description: Only meaningful for `number` (defaults to `formatted`); ignored otherwise.
        option_style:
          type:
            - string
            - "null"
          enum:
            - default
            - dot
            - null
          description: Only meaningful for list types (defaults to `default`); ignored otherwise.
        active:
          type: boolean
          description: Defaults to `true`.
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: "Metadados do usuário: objeto JSON livre. Envie `null` para limpar. Máx. 64 KB serializado, 16 níveis."
      required:
        - company_id
        - name
      additionalProperties: false
      description: Request body to create a custom classifier.
    CustomClassifierUpdateRequest:
      type: object
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 255
        field_type:
          type: string
          enum:
            - dropdown
            - radio
            - multi_select
            - number
            - text
            - phone
            - url
            - date
            - datetime
        number_format:
          type:
            - string
            - "null"
          enum:
            - formatted
            - unformatted
            - currency
            - percent
            - null
        option_style:
          type:
            - string
            - "null"
          enum:
            - default
            - dot
            - null
        active:
          type: boolean
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: "Metadados do usuário: objeto JSON livre. Envie `null` para limpar. Máx. 64 KB serializado, 16 níveis."
        custom_metadata_mode:
          type: string
          enum:
            - replace
            - merge
          description: "Como o `custom_metadata` enviado é reconciliado com o valor armazenado: `replace` (padrão) substitui o objeto inteiro, `merge` faz deep merge sobre o valor atual. Aceito apenas na atualização."
      additionalProperties: false
      description: "Request body to update a custom classifier: send only the fields to change."
    CustomClassifierValueCreateRequest:
      type: object
      properties:
        value:
          type: string
          minLength: 1
          maxLength: 255
        color:
          type: string
          pattern: ^#[0-9a-fA-F]{6}([0-9a-fA-F]{2})?$
          description: Hex swatch (`#rrggbb` or `#rrggbbaa`). Omit for an auto-assigned palette color.
          example: "#2a78d6"
        active:
          type: boolean
          description: Defaults to `true`.
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: "Metadados do usuário: objeto JSON livre. Envie `null` para limpar. Máx. 64 KB serializado, 16 níveis."
      required:
        - value
      additionalProperties: false
      description: Request body to create a custom classifier value.
    CustomClassifierValueUpdateRequest:
      type: object
      properties:
        value:
          type: string
          minLength: 1
          maxLength: 255
        color:
          type:
            - string
            - "null"
          pattern: ^#[0-9a-fA-F]{6}([0-9a-fA-F]{2})?$
          description: Hex swatch, or `null` to clear it and fall back to the palette.
          example: "#2a78d6"
        active:
          type: boolean
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: "Metadados do usuário: objeto JSON livre. Envie `null` para limpar. Máx. 64 KB serializado, 16 níveis."
        custom_metadata_mode:
          type: string
          enum:
            - replace
            - merge
          description: "Como o `custom_metadata` enviado é reconciliado com o valor armazenado: `replace` (padrão) substitui o objeto inteiro, `merge` faz deep merge sobre o valor atual. Aceito apenas na atualização."
      additionalProperties: false
      description: "Request body to update a custom classifier value: send only the fields to change."
    FiscalDocumentV1:
      type: object
      properties:
        id:
          type: string
          format: uuid
          example: 9f3c2b7a-51e4-4d0a-9c1e-2b8f0a7d6e33
        kind:
          type: string
          enum:
            - service
            - product
          description: "`service` is an NFS-e (nota de serviço); `product` is an NF-e (nota de produto)."
        situation:
          type: string
          enum:
            - authorized
            - cancelled
            - unknown
          description: Fiscal status as parsed from the document. `authorized` is a valid note; `cancelled` was cancelled at the origin; `unknown` when the source did not carry a determinable status.
        completeness:
          type: string
          enum:
            - full
            - summary
          description: "`full` when the whole document (items, taxes) was parsed; `summary` when only the header-level résumé is known (e.g. an NF-e known solely by its access-key summary event)."
        source:
          type: string
          enum:
            - nfe_io
            - xml_upload
            - sefaz
          description: Where the note entered the workspace. `nfe_io` — captured from the nfe.io provider; `xml_upload` — an XML someone imported; `sefaz` — fetched from SEFAZ by access key.
        direction:
          type: string
          enum:
            - inbound
            - outbound
          description: "`inbound` is a note received against the workspace's companies (the only kind the app parks here)."
        access_key:
          type: string
          description: The 44-digit fiscal access key (chave de acesso). Digits only.
          example: "35250814200166000187550010000104211099887766"
        number:
          type: string
          description: Document number as issued.
          example: "10421"
        series:
          type:
            - string
            - "null"
          description: Série. `null` on documents that carry none (e.g. most NFS-e).
          example: "1"
        municipality_code:
          type:
            - string
            - "null"
          description: IBGE municipality code of the service (NFS-e). `null` when not applicable.
          example: "4113700"
        nature_of_operation:
          type:
            - string
            - "null"
          description: Natureza da operação (NF-e). `null` when the document carries none.
          example: Venda de mercadoria
        issued_at:
          type: string
          format: date-time
          description: When the note was issued.
        competence_date:
          type:
            - string
            - "null"
          format: date-time
          description: Competência (NFS-e) — the service reference month. `null` when absent.
        issuer_name:
          type: string
          description: Legal name of the issuer (prestador / emitente).
          example: Kobana Serviços Digitais Ltda
        issuer_document:
          type: string
          description: Issuer CNPJ/CPF, digits only.
          example: "14200166000187"
        recipient_name:
          type:
            - string
            - "null"
          description: Legal name of the recipient (tomador / destinatário). `null` when the document omits it.
        recipient_document:
          type:
            - string
            - "null"
          description: Recipient CNPJ/CPF, digits only. `null` when absent.
          example: "28945310000104"
        recipient_state:
          type:
            - string
            - "null"
          description: Two-letter UF of the recipient. `null` when absent.
          example: SP
        company_id:
          type:
            - string
            - "null"
          format: uuid
          description: The workspace company this note was linked to (matched by recipient document). `null` when no company matched — the note is parked unlinked.
        total_amount:
          type: number
          description: Total value, in BRL.
          example: 4500
        net_amount:
          type:
            - number
            - "null"
          description: Net value after withholdings, as a number. `null` when the document carries no net figure.
          example: 4223.25
        payable_count:
          type: integer
          description: How many of the document's duplicates already generated a payable (conta a pagar) in the workspace.
        duplicate_count:
          type: integer
          description: Number of duplicates (parcelas) the document declares.
        xml_attachment_id:
          type:
            - string
            - "null"
          format: uuid
          description: The stored XML in the workspace attachment library. `null` when no XML is kept (e.g. a summary-only note).
        xml_download_url:
          type:
            - string
            - "null"
          description: Absolute URL for the stored XML bytes. `null` when there is no `xml_attachment_id`. ⚠️ It points at the attachments resource, so fetching it needs a token carrying `finance.attachments` — `finance.fiscal_documents` alone lists notes but does not download them.
          example: https://api.finance.kobana.com.br/v1/attachments/8c8b4c4f-2e0e-4f6e-9b8a-2a1c2b3d4e5f/download
        metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: Metadados do sistema, gerenciados pela Kobana. Somente leitura.
        custom_metadata:
          type:
            - object
            - "null"
          additionalProperties: {}
          description: "Metadados do usuário: objeto JSON livre."
        created_via:
          type:
            - string
            - "null"
          enum:
            - import
            - dashboard
            - api
            - cli
            - mcp
            - other
            - null
          description: "Channel that created the record: `dashboard`, `import`, `api`, `mcp`, `cli` or `other`. On API calls it is the `X-Request-Source` header. `null` when the change came from the system (scheduled jobs) or predates this tracking."
          example: api
        created_via_other:
          type:
            - string
            - "null"
          description: App that created the record when the channel is `other` (`X-Request-Source-Other`).
          example: null
        created_by_email:
          type:
            - string
            - "null"
          description: E-mail of the person who created the record. `null` for system changes.
          example: maria@empresa.com.br
        updated_via:
          type:
            - string
            - "null"
          enum:
            - import
            - dashboard
            - api
            - cli
            - mcp
            - other
            - null
          description: "Channel that last changed the record: `dashboard`, `import`, `api`, `mcp`, `cli` or `other`. On API calls it is the `X-Request-Source` header. `null` when the change came from the system (scheduled jobs) or predates this tracking."
          example: api
        updated_via_other:
          type:
            - string
            - "null"
          description: App that last changed the record when the channel is `other` (`X-Request-Source-Other`).
          example: null
        updated_by_email:
          type:
            - string
            - "null"
          description: E-mail of the person who last changed the record. `null` for system changes.
          example: maria@empresa.com.br
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
          description: "Last-write timestamp (ISO 8601). This is the incremental-sync window: poll `?updated_at[gte]=<end of your last poll>&sort=updated_at`."
      required:
        - id
        - kind
        - situation
        - completeness
        - source
        - direction
        - access_key
        - number
        - series
        - municipality_code
        - nature_of_operation
        - issued_at
        - competence_date
        - issuer_name
        - issuer_document
        - recipient_name
        - recipient_document
        - recipient_state
        - company_id
        - total_amount
        - net_amount
        - payable_count
        - duplicate_count
        - xml_attachment_id
        - xml_download_url
        - metadata
        - custom_metadata
        - created_via
        - created_via_other
        - created_by_email
        - updated_via
        - updated_via_other
        - updated_by_email
        - created_at
        - updated_at
      description: A received fiscal document (NF-e, NFS-e, CT-e) with its issuer, items, taxes and the payables it originated.
    FiscalDocumentListMeta:
      type: object
      properties:
        next_cursor:
          type:
            - string
            - "null"
          description: Opaque cursor. Pass back as `?cursor=…` to fetch the next page. `null` when there is no next page.
      required:
        - next_cursor
      description: "Pagination metadata of a received fiscal documents listing: `next_cursor` to pass as `cursor` for the next page (null on the last)."
    FiscalDocumentListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/FiscalDocumentV1"
        meta:
          $ref: "#/components/schemas/FiscalDocumentListMeta"
      required:
        - data
        - meta
      description: "One page of received fiscal documents: the records in `data` and the pagination in `meta`."
    DeletedResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            id:
              type: string
              format: uuid
              description: The id that was deleted.
            deleted:
              type: boolean
              enum:
                - true
          required:
            - id
            - deleted
      required:
        - data
      description: Answer of a successful delete, in `data`.
    IdSweepV1:
      type: object
      properties:
        data:
          type: array
          items:
            type: string
            format: uuid
          description: Ids visible to this token, ascending. Ordering is stable but carries no meaning.
        meta:
          type: object
          properties:
            next_cursor:
              type:
                - string
                - "null"
              description: Pass as `?cursor=`. `null` on the last page.
          required:
            - next_cursor
      required:
        - data
        - meta
      description: "One page of ids of a listing, for a full sweep without fetching the records: `data` holds the ids, `meta.next_cursor` the next page (null on the last)."
  parameters: {}
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: "Send `Authorization: Bearer <jwt>`. Signed RS256 by the Kobana authorization server (HS512 only against a local development server). Claims = { iss, aud, exp, jti, scope, sub, org }. **`org` is the tenant** — the organization's public id — while `sub` is the person who consented and scopes nothing. Pair it with the `X-Workspace-Id` header, which picks the workspace inside that organization."
paths:
  /financial-accounts:
    get:
      operationId: listFinancialAccounts
      summary: Listar contas financeiras
      description: Returns a cursor-paginated page of financial accounts for the workspace tied to the token's `sub` claim. Accounts may belong to a specific company or be workspace-level (`company_id` nullable). **The current balance is NOT returned by this listing** — the stored `balance` column is a write-only seed; the real balance is derived from the transactions ledger and will be available on the detail endpoint. Requires scope `finance.accounts`.
      tags:
        - Contas Financeiras
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Filter by owning company UUID. Accepts `[in]=uuid1,uuid2`. Workspace-level accounts (`company_id IS NULL`) are returned only when this filter is omitted.
            example: 62057dab-0f98-4020-ae1e-2c0803251b1e
          required: false
          name: company_id
          in: query
        - schema:
            type: string
            enum:
              - checking
              - savings
              - cash
              - investment
              - credit
            description: Account kind. Accepts `[in]=checking,savings`.
          required: false
          name: type
          in: query
        - schema:
            type: string
            format: uuid
            description: Filter by bank UUID. Accepts `[in]` for multi-bank lookups.
          required: false
          name: bank_id
          in: query
        - schema:
            type: string
            enum:
              - "true"
              - "false"
            description: Filter by active flag.
          required: false
          name: active
          in: query
        - schema:
            type: string
            description: Filter by exact account name. Accepts `[in]`.
          required: false
          name: name
          in: query
        - schema:
            type: string
            enum:
              - created_at
              - name
              - type
            description: "Field to order by. Default: `sort_by=name&sort_order=asc`."
          required: false
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: Order direction. Defaults to `asc`; ignored without `sort_by`.
          required: false
          name: sort_order
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            description: Page size. Default 25, max 100.
            example: 25
          required: false
          name: limit
          in: query
        - schema:
            type: string
            description: Opaque cursor for the next page. Round-trip `meta.next_cursor` from the previous response.
          required: false
          name: cursor
          in: query
        - schema:
            type: string
            description: Comma-separated top-level fields to return, e.g. `fields=description,amount,due_date`. Omit for every field. `id` is always included; naming an object field returns it whole. An unknown name is a 400 whose `details` lists `unknown` and `available`.
            example: description,amount,due_date
          required: false
          name: fields
          in: query
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: Page of accounts plus pagination metadata. No `balance` field by design.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/FinancialAccountListResponse"
        "400":
          description: Malformed query (unknown filter / operator, invalid limit, bad cursor).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token authenticated but lacks the `finance.accounts` scope, or `sub` does not match any workspace.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
    post:
      operationId: createFinancialAccount
      summary: Criar conta financeira
      description: "Creates a financial account. Requires scope `finance.accounts.write`. `workspace_id` is taken from the token. `company_id` is required (accounts must belong to a company; workspace-level accounts exist from imports but aren't creatable via v1). `balance` is an opening seed (default `0.00`) — it is NOT the live balance (that is computed from transactions and returned on the detail endpoint) and cannot be updated. The bank may be given as `bank_id` (UUID) or `bank_code` (public COMPE/BACEN, e.g. `341`) — not both (400); an unknown code → 422. The response echoes the resolved bank as `bank: { code, name }`. Pass an `X-Idempotency-Key` header on retries."
      tags:
        - Contas Financeiras
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            description: "Opaque idempotency token. Same key + same body replays the cached response with `X-Idempotent-Replay: true`; same key + different body returns 409. Scoped per workspace; the fingerprint covers the request method, path, and raw body."
          required: false
          name: X-Idempotency-Key
          in: header
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/FinancialAccountCreateRequest"
            examples:
              bank:
                summary: A bank account with an opening balance
                value:
                  company_id: 11111111-1111-1111-1111-111111111111
                  name: Conta Corrente Itaú
                  type: checking
                  bank_id: 22222222-2222-2222-2222-222222222222
                  agency: "1234"
                  account_number: "56789"
                  account_digit: "0"
                  balance: 1500
              cash:
                summary: A cash account
                value:
                  company_id: 11111111-1111-1111-1111-111111111111
                  name: Caixa
                  type: cash
      responses:
        "201":
          description: Account created. The body is the detail shape (includes the computed `balance`).
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/FinancialAccountDetailV1"
                required:
                  - data
        "400":
          description: Malformed JSON body, or `bank_id` and `bank_code` sent together.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.accounts.write` scope.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "409":
          description: "`X-Idempotency-Key` reused with a different body."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: Schema validation failure, `company_id` not in the tenant, or unknown `bank_code`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
  /financial-accounts/{id}:
    get:
      operationId: getFinancialAccount
      summary: Obter conta financeira por ID
      description: "Returns the account plus the **computed `balance`** (live, derived from posted transactions — not the creation seed; #13). The listing still omits balance. Returns the same shape as the rows from the list endpoint. Requires scope `finance.accounts`. 404 covers both \"doesn't exist\" and \"not in the tenant\" — the API does not distinguish."
      tags:
        - Contas Financeiras
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
          required: true
          name: id
          in: path
        - schema:
            type: string
            description: Comma-separated top-level fields to return, e.g. `fields=description,amount,due_date`. Omit for every field. `id` is always included; naming an object field returns it whole. An unknown name is a 400 whose `details` lists `unknown` and `available`.
            example: description,amount,due_date
          required: false
          name: fields
          in: query
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: Get a financial account by id
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/FinancialAccountDetailV1"
                required:
                  - data
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.accounts` scope, or `sub` does not match any workspace.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Account not found within the tenant.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
    patch:
      operationId: updateFinancialAccount
      summary: Atualizar conta financeira
      description: "Partial update. Requires scope `finance.accounts.write`. `balance` is never settable (the live balance is computed). Set `active: false` to soft-deactivate (existing transactions stay valid). The bank accepts `bank_id` or `bank_code` (not both → 400; unknown code → 422); the response echoes `bank: { code, name }`."
      tags:
        - Contas Financeiras
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
          required: true
          name: id
          in: path
        - schema:
            type: string
            description: "Opaque idempotency token. Same key + same body replays the cached response with `X-Idempotent-Replay: true`; same key + different body returns 409. Scoped per workspace; the fingerprint covers the request method, path, and raw body."
          required: false
          name: X-Idempotency-Key
          in: header
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/FinancialAccountUpdateRequest"
            examples:
              deactivate:
                summary: Soft-deactivate
                value:
                  active: false
              rename:
                summary: Rename + toggle reconcile
                value:
                  name: Conta Itaú (PJ)
                  disable_reconcile: true
      responses:
        "200":
          description: Updated account (detail shape with computed balance).
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/FinancialAccountDetailV1"
                required:
                  - data
        "400":
          description: Malformed JSON body, or `bank_id` and `bank_code` sent together.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.accounts.write` scope.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Account not found within the tenant.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "409":
          description: "`X-Idempotency-Key` reused with a different body."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: Schema validation failure, `company_id` not in the tenant, or unknown `bank_code`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
    delete:
      operationId: deleteFinancialAccount
      summary: Excluir conta financeira
      description: "Deletes an account. Requires scope `finance.accounts.write`. Returns 422 (`reason: in_use`, with a `usage` breakdown) when the account has any transaction, payable or receivable — deleting would cascade-delete its transactions, so soft-deactivate via `PATCH { active: false }` instead. Calling DELETE twice returns 200 then 404."
      tags:
        - Contas Financeiras
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
          required: true
          name: id
          in: path
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: Account deleted.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DeletedResponse"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.accounts.write` scope.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Account not found within the tenant.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: "The account is in use (`reason: in_use`)."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
  /financial-accounts/{id}/adjust-balance:
    post:
      operationId: adjustAccountBalance
      summary: Ajustar saldo da conta
      description: Corrects the account's calculated balance to `target_balance` by inserting an auto-created adjustment transaction for the signed diff. Requires scope `finance.accounts.write`. The `category_id` type must match the diff direction (credit for a positive diff, debit for negative) and its company must match the account's. A target equal to the current balance is rejected (422). Pass an `X-Idempotency-Key` header on retries.
      tags:
        - Contas Financeiras
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
          required: true
          name: id
          in: path
        - schema:
            type: string
            description: Opaque idempotency token. Same key + same body replays the cached response; same key + different body returns 409.
          required: false
          name: X-Idempotency-Key
          in: header
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AdjustBalanceRequest"
            examples:
              topUp:
                summary: Set the balance to R$ 1.500,00
                value:
                  target_balance: 1500
                  category_id: c1b2c3d4-0000-4000-8000-000000000001
      responses:
        "201":
          description: Adjustment created. The `Location` header points at the auto-created adjustment transaction (`GET /financial-transactions/{transaction_id}`).
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/AdjustBalanceResultV1"
                required:
                  - data
        "400":
          description: Malformed JSON body.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.accounts.write` scope.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Account or category not found within the tenant.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "409":
          description: "`X-Idempotency-Key` reuse with a different body."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: "Validation failure: target equals current balance, account without a company, or a category of the wrong type/company."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
  /financial-accounts/{id}/balances:
    get:
      operationId: listAccountBalances
      summary: Listar saldos registrados da conta
      description: Returns a cursor-paginated page of the account's recorded balances, newest `capture_date` first. **These are observations, not the computed balance** — the transaction-derived number is `balance` on `GET /financial-accounts/{id}`. Each row carries the `source` that produced it, so a balance the bank reported and one a person typed stay distinguishable. Requires scope `finance.accounts`.
      tags:
        - Contas Financeiras
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
          required: true
          name: id
          in: path
        - schema:
            type: string
            enum:
              - calculated
              - banking
              - manual
              - imported
              - api
              - mcp
              - cli
            description: Filter by the channel that produced the observation. Accepts `[in]=banking,manual`.
            example: banking
          required: false
          name: source
          in: query
        - schema:
            type: string
            description: Filter on the observation instant. Accepts the range operators `[gte]` / `[lte]` / `[gt]` / `[lt]`.
            example: 2026-09-01T00:00:00Z
          required: false
          name: capture_date
          in: query
        - schema:
            type: string
            description: Filter by your own id for the observation. Accepts `[in]`.
          required: false
          name: external_id
          in: query
        - schema:
            type: string
            enum:
              - capture_date
              - created_at
              - amount
            description: Default `capture_date`.
          required: false
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: Default `desc` — the newest observation first.
          required: false
          name: sort_order
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            description: Default 25.
          required: false
          name: limit
          in: query
        - schema:
            type: string
            description: Round-trip `meta.next_cursor` from the previous response.
          required: false
          name: cursor
          in: query
        - schema:
            type: string
            description: Comma-separated top-level fields to return, e.g. `fields=description,amount,due_date`. Omit for every field. `id` is always included; naming an object field returns it whole. An unknown name is a 400 whose `details` lists `unknown` and `available`.
            example: description,amount,due_date
          required: false
          name: fields
          in: query
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: Page of recorded balances plus pagination metadata.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AccountBalanceListResponse"
              examples:
                page:
                  summary: One balance from the banking gateway
                  value:
                    data:
                      - id: 9f1c0b6e-4b23-4a2b-9c3e-6a0c1a2b3c4d
                        financial_account_id: fe023ef2-a70f-4a7c-a413-abdfe4977a19
                        source: banking
                        capture_date: 2026-09-10T01:34:41.000Z
                        amount: 1530.45
                        blocked_amount: 24
                        automatically_invested_amount: 50
                        external_id: null
                        tags: []
                        notes: null
                        metadata:
                          banking_gateway:
                            id: 01a08886-0b28-7bb5-89d1-30a2a4c444f5
                            payload:
                              uid: 01a08886-0b28-7bb5-89d1-30a2a4c444f5
                              amount: 1530.45
                              capture_date: 2026-09-10T01:34:41.000Z
                        custom_metadata: null
                        created_at: 2026-09-10T01:35:02.000Z
                    meta:
                      next_cursor: null
        "400":
          description: Malformed query (unknown filter / operator, invalid limit, bad cursor).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.accounts` scope.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Account not found within the tenant.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
    post:
      operationId: createAccountBalance
      summary: Registrar um saldo
      description: Records one observed balance for the account. This does NOT move money and creates no transaction — use `POST /financial-accounts/{id}/adjust-balance` when you want the ledger corrected. `source` defaults to `api`; narrow it to `mcp` or `cli` so the audit trail names the real channel. The sources the product produces itself (`manual`, `banking`, `calculated`, `imported`) are refused (422). Re-sending the same `external_id` for the same account and source returns 409. The new row's id is the `balanceId` used by the delete. Requires scope `finance.accounts.write`; pass an `X-Idempotency-Key` header on retries.
      tags:
        - Contas Financeiras
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
          required: true
          name: id
          in: path
        - schema:
            type: string
            description: Opaque idempotency token. Same key + same body replays the cached response; same key + different body returns 409.
          required: false
          name: X-Idempotency-Key
          in: header
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AccountBalanceCreateRequest"
            examples:
              fromMcp:
                summary: Balance read by an MCP client
                value:
                  amount: 1234.56
                  capture_date: 2026-09-10T12:00:00Z
                  source: mcp
                  external_id: ledger-2026-09-10
              withSlices:
                summary: Balance with the blocked and auto-invested slices
                value:
                  amount: 1530.45
                  blocked_amount: 24
                  automatically_invested_amount: 50
                  notes: Fechamento do dia
      responses:
        "201":
          description: Balance recorded. `Location` points at the new row.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/AccountBalanceV1"
                required:
                  - data
              examples:
                created:
                  summary: Recorded balance
                  value:
                    data:
                      id: 9f1c0b6e-4b23-4a2b-9c3e-6a0c1a2b3c4d
                      financial_account_id: fe023ef2-a70f-4a7c-a413-abdfe4977a19
                      source: banking
                      capture_date: 2026-09-10T01:34:41.000Z
                      amount: 1530.45
                      blocked_amount: 24
                      automatically_invested_amount: 50
                      external_id: null
                      tags: []
                      notes: null
                      metadata:
                        banking_gateway:
                          id: 01a08886-0b28-7bb5-89d1-30a2a4c444f5
                          payload:
                            uid: 01a08886-0b28-7bb5-89d1-30a2a4c444f5
                            amount: 1530.45
                            capture_date: 2026-09-10T01:34:41.000Z
                      custom_metadata: null
                      created_at: 2026-09-10T01:35:02.000Z
        "400":
          description: Malformed JSON body.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.accounts.write` scope.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Account not found within the tenant.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "409":
          description: "`X-Idempotency-Key` reuse with a different body, or an `external_id` already used for this account and source."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: Validation failure, including a `source` the product produces itself.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
  /financial-accounts/{id}/balances/latest:
    get:
      operationId: getLatestAccountBalances
      summary: Último saldo de cada origem
      description: "Returns the most recent observation of each source in one call — what a source picker needs without one request per source. A source that never reported comes back with `amount: null`, which is deliberately different from a source that reported zero. `calculated` is always recomputed from the posted transactions rather than read from its stored snapshots, so it can never disagree with `balance` on the account detail. Requires scope `finance.accounts`."
      tags:
        - Contas Financeiras
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
          required: true
          name: id
          in: path
        - schema:
            type: string
            description: Comma-separated top-level fields to return, e.g. `fields=description,amount,due_date`. Omit for every field. `id` is always included; naming an object field returns it whole. An unknown name is a 400 whose `details` lists `unknown` and `available`.
            example: description,amount,due_date
          required: false
          name: fields
          in: query
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: One entry per source.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AccountBalanceLatestListResponse"
              examples:
                latest:
                  summary: Bank and dashboard disagree by R$ 30,00
                  value:
                    data:
                      - source: calculated
                        amount: 1500.45
                        blocked_amount: null
                        automatically_invested_amount: null
                        capture_date: null
                        count: 12
                      - source: banking
                        amount: 1530.45
                        blocked_amount: 24
                        automatically_invested_amount: 50
                        capture_date: 2026-09-10T01:34:41.000Z
                        count: 48
                      - source: manual
                        amount: null
                        blocked_amount: null
                        automatically_invested_amount: null
                        capture_date: null
                        count: 0
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.accounts` scope.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Account not found within the tenant.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
  /financial-accounts/{id}/balances/{balanceId}:
    delete:
      operationId: deleteAccountBalance
      summary: Excluir saldo registrado
      description: "Removes one recorded balance. A `banking` or `calculated` row is refused (422): both are reproducible from their producer, so the delete would be undone by the next import or snapshot. Requires scope `finance.accounts.write`."
      tags:
        - Contas Financeiras
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
          required: true
          name: id
          in: path
        - schema:
            type: string
            format: uuid
          required: true
          name: balanceId
          in: path
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: Balance removed.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DeletedResponse"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.accounts.write` scope.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Balance not found within the tenant.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: The row's source is reproducible and cannot be deleted.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
  /payables/aggregates:
    get:
      operationId: aggregatePayables
      summary: Somar totais de contas a pagar
      description: "Sums payables for the workspace tied to the token: `count`, `total`, `pending`, `paid`, `overdue_count`. Requires scope `finance.payables`. Filter by `company_id` and a period; omit all filters for all-time."
      tags:
        - Contas a Pagar
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Scope to one company. Omit to aggregate across the whole tenant.
          required: false
          name: company_id
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 12
            description: Calendar month (1–12). Requires `year`. Mutually exclusive with `start_date`/`end_date`.
          required: false
          name: month
          in: query
        - schema:
            type:
              - integer
              - "null"
            description: Calendar year. Requires `month`.
            example: 2026
          required: false
          name: year
          in: query
        - schema:
            type: string
            description: Window start (ISO 8601). Requires `end_date`. Mutually exclusive with `month`/`year`.
            example: 2026-07-01
          required: false
          name: start_date
          in: query
        - schema:
            type: string
            description: Window end (ISO 8601). Requires `start_date`.
            example: 2026-07-31
          required: false
          name: end_date
          in: query
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: Payable totals for the scope.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/PayableAggregatesV1"
                required:
                  - data
        "400":
          description: Malformed query (unknown param, bad pairing, invalid date/uuid).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.payables` scope, or `sub` does not match any workspace.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
  /receivables/aggregates:
    get:
      operationId: aggregateReceivables
      summary: Somar totais de contas a receber
      description: "Sums receivables for the workspace tied to the token: `count`, `total`, `pending`, `received`, `overdue_count`. Requires scope `finance.receivables`. Filter by `company_id` and a period; omit all filters for all-time."
      tags:
        - Contas a Receber
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Scope to one company. Omit to aggregate across the whole tenant.
          required: false
          name: company_id
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 12
            description: Calendar month (1–12). Requires `year`. Mutually exclusive with `start_date`/`end_date`.
          required: false
          name: month
          in: query
        - schema:
            type:
              - integer
              - "null"
            description: Calendar year. Requires `month`.
            example: 2026
          required: false
          name: year
          in: query
        - schema:
            type: string
            description: Window start (ISO 8601). Requires `end_date`. Mutually exclusive with `month`/`year`.
            example: 2026-07-01
          required: false
          name: start_date
          in: query
        - schema:
            type: string
            description: Window end (ISO 8601). Requires `start_date`.
            example: 2026-07-31
          required: false
          name: end_date
          in: query
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: Receivable totals for the scope.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/ReceivableAggregatesV1"
                required:
                  - data
        "400":
          description: Malformed query (unknown param, bad pairing, invalid date/uuid).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.receivables` scope, or `sub` does not match any workspace.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
  /financial-transactions/aggregates:
    get:
      operationId: aggregateTransactions
      summary: Somar totais de transações
      description: "Sums realised transactions for the workspace tied to the token (forecasts excluded): `count`, `credit`, `debit`, `net`. Requires scope `finance.transactions`. Filter by `company_id` and a period; omit all filters for all-time."
      tags:
        - Transações Financeiras
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Scope to one company. Omit to aggregate across the whole tenant.
          required: false
          name: company_id
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 12
            description: Calendar month (1–12). Requires `year`. Mutually exclusive with `start_date`/`end_date`.
          required: false
          name: month
          in: query
        - schema:
            type:
              - integer
              - "null"
            description: Calendar year. Requires `month`.
            example: 2026
          required: false
          name: year
          in: query
        - schema:
            type: string
            description: Window start (ISO 8601). Requires `end_date`. Mutually exclusive with `month`/`year`.
            example: 2026-07-01
          required: false
          name: start_date
          in: query
        - schema:
            type: string
            description: Window end (ISO 8601). Requires `start_date`.
            example: 2026-07-31
          required: false
          name: end_date
          in: query
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: Transaction totals for the scope.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/TransactionAggregatesV1"
                required:
                  - data
        "400":
          description: Malformed query (unknown param, bad pairing, invalid date/uuid).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.transactions` scope, or `sub` does not match any workspace.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
  /attachments:
    get:
      operationId: listAttachments
      summary: Listar anexos
      description: Returns a cursor-paginated page of attachment metadata for the workspace tied to the token's `sub` claim. Each row carries an absolute `download_url` pointing at the download endpoint; fetching the bytes requires the same Bearer JWT (scope `finance.attachments`). Aggregate counts (`linked_payables_count` etc.) expose how many parent rows reference each blob without forcing the client to call every parent endpoint. Default sort is `uploaded_at` descending (newest first). Requires scope `finance.attachments`.
      tags:
        - Anexos
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            description: Filter by exact MIME type. Accepts `[in]=application/pdf,image/png`.
            example: application/pdf
          required: false
          name: mime_type
          in: query
        - schema:
            type: string
            description: Filter by exact file name. Accepts `[in]`.
          required: false
          name: file_name
          in: query
        - schema:
            type: string
            description: Filter by uploader email. Accepts `[in]`.
          required: false
          name: uploaded_by
          in: query
        - schema:
            type: string
            description: "ISO date filter on upload time. Range operators: `[gte]`, `[lte]`, `[gt]`, `[lt]`."
          required: false
          name: uploaded_at
          in: query
        - schema:
            type: string
            format: uuid
            description: Return only attachments linked to this payable. Single UUID via eq.
          required: false
          name: linked_to_payable_id
          in: query
        - schema:
            type: string
            format: uuid
            description: Return only attachments linked to this receivable. Single UUID via eq.
          required: false
          name: linked_to_receivable_id
          in: query
        - schema:
            type: string
            format: uuid
            description: Return only attachments linked to this transaction. Single UUID via eq.
          required: false
          name: linked_to_transaction_id
          in: query
        - schema:
            type: string
            enum:
              - file_name
              - file_size
              - uploaded_at
            description: Field to order by.
          required: false
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: Order direction. Defaults to `asc`; ignored without `sort_by`.
          required: false
          name: sort_order
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            description: Page size. Default 25, max 100.
          required: false
          name: limit
          in: query
        - schema:
            type: string
            description: Opaque cursor for the next page. Round-trip `meta.next_cursor` from the previous response.
          required: false
          name: cursor
          in: query
        - schema:
            type: string
            description: Comma-separated top-level fields to return, e.g. `fields=description,amount,due_date`. Omit for every field. `id` is always included; naming an object field returns it whole. An unknown name is a 400 whose `details` lists `unknown` and `available`.
            example: description,amount,due_date
          required: false
          name: fields
          in: query
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: Page of attachments plus pagination metadata.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AttachmentListResponse"
        "400":
          description: Malformed query (unknown filter / operator, invalid limit, bad cursor).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token authenticated but lacks the `finance.attachments` scope, or `sub` does not match any workspace.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
    post:
      operationId: uploadAttachment
      summary: Enviar um anexo
      description: "Uploads a file to the workspace blob registry. Send as `multipart/form-data` with a `file` part and an optional `title`. Requires scope `finance.attachments.write`. Allowed types: pdf, png, jpg/jpeg, xml, ofx, csv, xlsx; max 10 MB. For PDF/PNG/JPEG the declared MIME must match the payload's magic bytes (mismatch → 422). The response is the same metadata shape as `GET /attachments/{id}`; link counts start at 0 — associate the file via `POST /attachments/{id}/links`. `custom_metadata` may ride along as a JSON string in the multipart body."
      tags:
        - Anexos
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            description: "Opaque idempotency token. The fingerprint covers the raw bytes, so a multipart upload replays like any other write: same key + same file replays the recorded response with `X-Idempotent-Replay: true`; same key + a different file returns 409."
          required: false
          name: X-Idempotency-Key
          in: header
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              $ref: "#/components/schemas/AttachmentUploadRequest"
      responses:
        "201":
          description: Attachment uploaded. The `Location` header points at the new row.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/AttachmentV1"
                required:
                  - data
        "400":
          description: Body is not valid multipart/form-data.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.attachments.write` scope.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "409":
          description: "`X-Idempotency-Key` reused with a different body."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: Missing `file` part, file too large, disallowed MIME type, or magic bytes that don't match the declared type.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
  /attachments/{id}/download:
    get:
      operationId: downloadAttachment
      summary: Baixar bytes do anexo
      description: "Streams the file bytes for the attachment back to the caller. Requires scope `finance.attachments` — the same Bearer JWT as the listing endpoint. `Content-Type` reflects the stored MIME; `Content-Disposition` is `inline` for PDF / images and `attachment` for other types. No caching — `Cache-Control: private, no-store`."
      tags:
        - Anexos
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
          required: true
          name: id
          in: path
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: File bytes (binary stream). See `Content-Type` and `Content-Disposition` headers.
          content:
            application/octet-stream:
              schema:
                type: string
                format: binary
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token authenticated but lacks the `finance.attachments` scope, or `sub` does not match any workspace.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Attachment not found within the tenant.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
  /attachments/{id}:
    get:
      operationId: getAttachment
      summary: Obter metadados do anexo por ID
      description: Returns the metadata row including `download_url`. Fetching the bytes still requires `GET /attachments/{id}/download` with the same scope. Returns the same shape as the rows from the list endpoint. Requires scope `finance.attachments`. 404 covers both "doesn't exist" and "not in the tenant" — the API does not distinguish.
      tags:
        - Anexos
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
          required: true
          name: id
          in: path
        - schema:
            type: string
            description: Comma-separated top-level fields to return, e.g. `fields=description,amount,due_date`. Omit for every field. `id` is always included; naming an object field returns it whole. An unknown name is a 400 whose `details` lists `unknown` and `available`.
            example: description,amount,due_date
          required: false
          name: fields
          in: query
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: Get attachment metadata by id
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/AttachmentV1"
                required:
                  - data
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.attachments` scope, or `sub` does not match any workspace.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Attachment not found within the tenant.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
    delete:
      operationId: deleteAttachment
      summary: Excluir anexo
      description: "Deletes the attachment row and its stored bytes. Requires scope `finance.attachments.write`. Returns 409 (`CONFLICT`, with per-entity link counts in `details`) when the file is still linked to any payable / receivable / transaction — unlink it first via `DELETE /attachments/{id}/links`. Also 409 (`details.receipts`) when the file is a payment receipt's document: there is no unlink for that, delete the receipt instead and the file goes with it. Calling DELETE twice returns 200 then 404."
      tags:
        - Anexos
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
          required: true
          name: id
          in: path
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: Attachment deleted.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DeletedResponse"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.attachments.write` scope.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Attachment not found within the tenant.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "409":
          description: The attachment is still linked to one or more records, or is a payment receipt's document (`details` carries the counts).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
  /attachments/{id}/links:
    post:
      operationId: linkAttachment
      summary: Vincular anexo a um registro
      description: Associates an existing attachment with a payable / receivable / transaction. The `{id}` path segment is the attachment; the body identifies the parent. Requires scope `finance.attachments.write`. Returns 409 when the association already exists, 404 when either the attachment or the parent is outside the tenant.
      tags:
        - Anexos
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
          required: true
          name: id
          in: path
        - schema:
            type: string
            description: "Opaque idempotency token. Same key + same body replays the cached response with `X-Idempotent-Replay: true`; same key + different body returns 409. Scoped per workspace; the fingerprint covers the request method, path, and raw body."
          required: false
          name: X-Idempotency-Key
          in: header
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AttachmentLinkRequest"
            examples:
              payable:
                summary: Link to a payable
                value:
                  type: payable
                  id: 8c8b4c4f-2e0e-4f6e-9b8a-2a1c2b3d4e5f
              transaction:
                summary: Link to a transaction
                value:
                  type: transaction
                  id: 3c8b4c4f-2e0e-4f6e-9b8a-2a1c2b3d4e5f
      responses:
        "201":
          description: Association created. Echoes `{ attachment_id, parent_type, parent_id }`.
          content:
            application/json:
              schema:
                type: object
                properties:
                  attachment_id:
                    type: string
                    format: uuid
                  parent_type:
                    type: string
                    enum:
                      - payable
                      - receivable
                      - transaction
                  parent_id:
                    type: string
                    format: uuid
                required:
                  - attachment_id
                  - parent_type
                  - parent_id
        "400":
          description: Malformed JSON body.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.attachments.write` scope.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Attachment or parent record not found within the tenant.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "409":
          description: The attachment is already linked to this record.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: Schema validation failure (unknown `type`, non-uuid `id`).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
    delete:
      operationId: unlinkAttachment
      summary: Desvincular anexo de um registro
      description: Removes a single association between the attachment (`{id}`) and the parent identified in the body. Requires scope `finance.attachments.write`. Returns 404 when no such association exists. The attachment row itself is left intact — delete it with `DELETE /attachments/{id}`.
      tags:
        - Anexos
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
          required: true
          name: id
          in: path
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AttachmentLinkRequest"
            examples:
              payable:
                summary: Unlink from a payable
                value:
                  type: payable
                  id: 8c8b4c4f-2e0e-4f6e-9b8a-2a1c2b3d4e5f
      responses:
        "200":
          description: Association removed.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DeletedResponse"
        "400":
          description: Malformed JSON body.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.attachments.write` scope.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: No association between this attachment and the given record.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: Schema validation failure (unknown `type`, non-uuid `id`).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
  /automatic-rules:
    get:
      operationId: listAutomaticRules
      summary: Listar regras automáticas
      description: Returns a cursor-paginated page of automatic (classification) rules for the workspace tied to the token's `sub` claim. Rules fire on freshly-created transactions/billings, first-match-wins within `(company, scope, position)`. Default order is the evaluation order `(company, scope, position)`. Requires scope `finance.automatic_rules`.
      tags:
        - Regras Automáticas
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Filter by owning company UUID. Accepts `[in]=uuid1,uuid2` for OR matching.
            example: 62057dab-0f98-4020-ae1e-2c0803251b1e
          required: false
          name: company_id
          in: query
        - schema:
            type: string
            enum:
              - transactions
              - billings
            description: "Filter by target ledger: `transactions` or `billings`."
          required: false
          name: scope
          in: query
        - schema:
            type: boolean
            description: Filter by active flag. `?active=true` or `?active=false`.
          required: false
          name: active
          in: query
        - schema:
            type: string
            enum:
              - created_at
              - position
            description: "Field to order by. Default: `(company, scope, position)` ascending — the evaluation order."
          required: false
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: Order direction. Defaults to `asc`; ignored without `sort_by`.
          required: false
          name: sort_order
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            description: Page size. Default 25, max 100.
            example: 25
          required: false
          name: limit
          in: query
        - schema:
            type: string
            description: Opaque cursor for the next page. Round-trip `meta.next_cursor` from the previous response.
          required: false
          name: cursor
          in: query
        - schema:
            type: string
            description: Comma-separated top-level fields to return, e.g. `fields=description,amount,due_date`. Omit for every field. `id` is always included; naming an object field returns it whole. An unknown name is a 400 whose `details` lists `unknown` and `available`.
            example: description,amount,due_date
          required: false
          name: fields
          in: query
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: Page of rules plus pagination metadata.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AutomaticRuleListResponse"
        "400":
          description: Malformed query (unknown filter / operator, invalid limit, bad cursor).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token authenticated but lacks the `finance.automatic_rules` scope, or `sub` does not match any workspace.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
    post:
      operationId: createAutomaticRule
      summary: Criar regra automática
      description: Creates a rule and appends it at the end of its `(company, scope)` group (`position` = next). Requires scope `finance.automatic_rules.write`. `workspace_id` comes from the token. A rule needs at least one criterion and at least one action; `account_id` is only valid for `scope=transactions` and `set_expected_account_id` only for `scope=billings`. Pass an `X-Idempotency-Key` header on retries.
      tags:
        - Regras Automáticas
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            description: "Opaque idempotency token. Same key + same body replays the cached response with `X-Idempotent-Replay: true`; same key + different body returns 409. Scoped per workspace."
          required: false
          name: X-Idempotency-Key
          in: header
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AutomaticRuleCreateRequest"
            examples:
              categorizeBySupplier:
                summary: "On transactions: set category when description contains a keyword"
                value:
                  company_id: 11111111-1111-1111-1111-111111111111
                  scope: transactions
                  description_contains: uber
                  set_category_id: 22222222-2222-2222-2222-222222222222
              routeBillings:
                summary: "On billings: set the expected settlement account for a supplier"
                value:
                  company_id: 11111111-1111-1111-1111-111111111111
                  scope: billings
                  match_person_id: 33333333-3333-3333-3333-333333333333
                  set_expected_account_id: 44444444-4444-4444-4444-444444444444
      responses:
        "201":
          description: Rule created. The `Location` header points at the new row.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/AutomaticRuleV1"
                required:
                  - data
        "400":
          description: Malformed JSON body.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.automatic_rules.write` scope.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "409":
          description: "`X-Idempotency-Key` reused with a different body."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: Schema validation failure; `company_id` or a referenced entity not in the tenant; no criterion / no action; or a scope↔field mismatch.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
  /automatic-rules/{id}:
    get:
      operationId: getAutomaticRule
      summary: Obter regra automática por ID
      description: " Returns the same shape as the rows from the list endpoint. Requires scope `finance.automatic_rules`. 404 covers both \"doesn't exist\" and \"not in the tenant\" — the API does not distinguish."
      tags:
        - Regras Automáticas
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
          required: true
          name: id
          in: path
        - schema:
            type: string
            description: Comma-separated top-level fields to return, e.g. `fields=description,amount,due_date`. Omit for every field. `id` is always included; naming an object field returns it whole. An unknown name is a 400 whose `details` lists `unknown` and `available`.
            example: description,amount,due_date
          required: false
          name: fields
          in: query
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: Get an automatic rule by id
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/AutomaticRuleV1"
                required:
                  - data
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.automatic_rules` scope, or `sub` does not match any workspace.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Rule not found within the tenant.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
    patch:
      operationId: updateAutomaticRule
      summary: Atualizar regra automática
      description: Partial update of criteria/actions and `active`. Requires scope `finance.automatic_rules.write`. `scope` and `company_id` are immutable (delete + recreate to move a rule); `position` is changed via `POST /automatic-rules/reorder`. The merged rule must still have at least one criterion and one action.
      tags:
        - Regras Automáticas
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
          required: true
          name: id
          in: path
        - schema:
            type: string
            description: "Opaque idempotency token. Same key + same body replays the cached response with `X-Idempotent-Replay: true`; same key + different body returns 409. Scoped per workspace; the fingerprint covers the request method, path, and raw body."
          required: false
          name: X-Idempotency-Key
          in: header
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AutomaticRuleUpdateRequest"
            examples:
              deactivate:
                summary: Pause a rule
                value:
                  active: false
              retarget:
                summary: Change the assigned category
                value:
                  set_category_id: 55555555-5555-5555-5555-555555555555
      responses:
        "200":
          description: Updated rule.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/AutomaticRuleV1"
                required:
                  - data
        "400":
          description: Malformed JSON body.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.automatic_rules.write` scope.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Rule not found within the tenant.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "409":
          description: "`X-Idempotency-Key` reused with a different body."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: Schema validation failure; a referenced entity not in the tenant; or the merged rule loses its last criterion/action.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
    delete:
      operationId: deleteAutomaticRule
      summary: Excluir regra automática
      description: Hard-deletes a rule. Requires scope `finance.automatic_rules.write`. Surviving rules keep their positions (a gap is left — reorder to compact). Calling DELETE twice returns 200 then 404.
      tags:
        - Regras Automáticas
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
          required: true
          name: id
          in: path
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: Rule deleted.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DeletedResponse"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.automatic_rules.write` scope.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Rule not found within the tenant.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
  /automatic-rules/reorder:
    post:
      operationId: reorderAutomaticRules
      summary: Reordenar regras automáticas
      description: "Rewrites `position` for one `(company_id, scope)` group so it matches `ordered_ids` (first → position 1). Requires scope `finance.automatic_rules.write`. `ordered_ids` must be the COMPLETE ordered set for that group — a partial or foreign set is rejected (422). Idempotent via `X-Idempotency-Key`. Answers `200 { data: { ordered_ids } }`."
      tags:
        - Regras Automáticas
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            description: Opaque idempotency token. Same key + same body replays; same key + different body returns 409.
          required: false
          name: X-Idempotency-Key
          in: header
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AutomaticRuleReorderRequest"
            examples:
              reorder:
                summary: Put rule B before rule A
                value:
                  company_id: 11111111-1111-1111-1111-111111111111
                  scope: transactions
                  ordered_ids:
                    - bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb
                    - aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa
      responses:
        "200":
          description: Positions rewritten. Echoes the ids in the order they now hold.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      ordered_ids:
                        type: array
                        items:
                          type: string
                          format: uuid
                        description: The rule ids, first → `position` 1.
                    required:
                      - ordered_ids
                required:
                  - data
        "400":
          description: Malformed JSON body.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.automatic_rules.write` scope.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "409":
          description: "`X-Idempotency-Key` reused with a different body."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: Schema validation failure; `company_id` not in the tenant; or `ordered_ids` is not the exact set for `(company, scope)`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
  /banks:
    get:
      operationId: listBanks
      summary: Listar bancos
      description: Returns a cursor-paginated page of the global bank catalog (COMPE/BACEN codes). Requires scope `finance.banks`. Tenant-less reference data — every token sees the same rows. Defaults to alphabetical sort by `name`.
      tags:
        - Bancos
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            description: Filter by exact COMPE/BACEN code (unique). Accepts `[in]=001,237,341` for OR matching.
            example: "341"
          required: false
          name: code
          in: query
        - schema:
            type: string
            description: Filter by exact institution name. Accepts `[in]=...` for OR matching.
            example: Itaú Unibanco S.A.
          required: false
          name: name
          in: query
        - schema:
            type: string
            enum:
              - code
              - name
            description: "Field to order by. Default: `sort_by=name&sort_order=asc`."
          required: false
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: Order direction. Defaults to `asc`; ignored without `sort_by`.
          required: false
          name: sort_order
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            description: Page size. Default 25, max 100.
            example: 25
          required: false
          name: limit
          in: query
        - schema:
            type: string
            description: Opaque cursor for the next page. Round-trip `meta.next_cursor` from the previous response.
          required: false
          name: cursor
          in: query
        - schema:
            type: string
            description: Comma-separated top-level fields to return, e.g. `fields=description,amount,due_date`. Omit for every field. `id` is always included; naming an object field returns it whole. An unknown name is a 400 whose `details` lists `unknown` and `available`.
            example: description,amount,due_date
          required: false
          name: fields
          in: query
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: Page of banks plus pagination metadata.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BankListResponse"
        "400":
          description: Malformed query (unknown filter / operator, invalid limit, bad cursor).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token authenticated but lacks the `finance.banks` scope, or `sub` does not match any workspace.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
  /cash-flow:
    get:
      operationId: getCashFlow
      summary: Obter a projeção de fluxo de caixa
      description: "Returns the cash-flow projection for the workspace tied to the token: a series of time buckets (realised + projected income/expenses, running balance) plus `current_balance`, the `today_bucket` seam and `retroactive_overdue`. Requires scope `finance.cash_flow`. Read-only."
      tags:
        - Fluxo de Caixa
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            description: Window start (ISO 8601). Required.
            example: 2026-01-01
          required: true
          name: from
          in: query
        - schema:
            type: string
            description: Window end (ISO 8601). Required.
            example: 2026-12-31
          required: true
          name: to
          in: query
        - schema:
            type: string
            enum:
              - day
              - week
              - month
              - year
            description: "Bucket size. Default `month`. A range over the per-granularity cap is clipped (`truncated: true`)."
          required: false
          name: granularity
          in: query
        - schema:
            type: string
            format: uuid
            description: Scope to one company. Omit for the consolidated (all-company) projection.
          required: false
          name: company_id
          in: query
        - schema:
            type: string
            format: uuid
            description: Scope to one financial account. Omit for all accounts.
          required: false
          name: account_id
          in: query
        - schema:
            type: string
            description: IANA time zone that anchors the `today` seam (e.g. `America/Sao_Paulo`, the default). Only the seam is tz-aware; bucketing stays UTC. An unrecognized value falls back to the default.
          required: false
          name: tz
          in: query
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: The cash-flow projection.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/CashFlowV1"
                required:
                  - data
        "400":
          description: Malformed query (missing/invalid `from`/`to`, bad granularity, non-uuid scope id, unknown param).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.cash_flow` scope, or `sub` does not match any workspace.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: The `company_id` or `account_id` is not in the tenant.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
  /categories:
    get:
      operationId: listCategories
      summary: Listar categorias
      description: Returns a cursor-paginated page of categories for the workspace tied to the token's `sub` claim. Company-scoped within the workspace; categories form a tree via `parent_id` but are returned flat — build the tree client-side using `parent_id` and `level`. Requires scope `finance.categories`.
      tags:
        - Categorias
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Filter by owning company UUID. Accepts `[in]=uuid1,uuid2` for OR matching.
            example: 62057dab-0f98-4020-ae1e-2c0803251b1e
          required: false
          name: company_id
          in: query
        - schema:
            type: string
            format: uuid
            description: Filter by direct parent UUID. To list root-level categories, use `?level=1` instead — the URL grammar does not support `parent_id=null`.
          required: false
          name: parent_id
          in: query
        - schema:
            type: string
            enum:
              - debit
              - credit
            description: "`debit` (cost) or `credit` (revenue). Accepts `[in]=debit,credit`."
          required: false
          name: type
          in: query
        - schema:
            type: integer
            minimum: 1
            description: "Filter by depth. `1` returns root-level categories. Range operators supported: `[gte]`, `[lte]`. Example: `?level[lte]=2` for roots + their direct children."
            example: 1
          required: false
          name: level
          in: query
        - schema:
            type: string
            description: Filter by exact name. Accepts `[in]`.
            example: Aluguel
          required: false
          name: name
          in: query
        - schema:
            type: string
            enum:
              - created_at
              - level
              - name
            description: "Field to order by. Default: `sort_by=name&sort_order=asc`."
          required: false
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: Order direction. Defaults to `asc`; ignored without `sort_by`.
          required: false
          name: sort_order
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            description: Page size. Default 25, max 100.
            example: 25
          required: false
          name: limit
          in: query
        - schema:
            type: string
            description: Opaque cursor for the next page. Round-trip `meta.next_cursor` from the previous response.
          required: false
          name: cursor
          in: query
        - schema:
            type: string
            description: Comma-separated top-level fields to return, e.g. `fields=description,amount,due_date`. Omit for every field. `id` is always included; naming an object field returns it whole. An unknown name is a 400 whose `details` lists `unknown` and `available`.
            example: description,amount,due_date
          required: false
          name: fields
          in: query
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: Page of categories plus pagination metadata.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CategoryListResponse"
        "400":
          description: Malformed query (unknown filter / operator, invalid limit, bad cursor).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token authenticated but lacks the `finance.categories` scope, or `sub` does not match any workspace.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
    post:
      operationId: createCategory
      summary: Criar categoria
      description: Creates a category. Requires scope `finance.categories.write`. `workspace_id` is taken from the token. Omit `parent_id` for a root (`level` 1); when set, the server derives `level = parent.level + 1` (max depth 3) and requires `type` to match the parent. The response is flat (`parent_id` + `level`). Pass an `X-Idempotency-Key` header on retries.
      tags:
        - Categorias
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            description: "Opaque idempotency token. Same key + same body replays the cached response with `X-Idempotent-Replay: true`; same key + different body returns 409. Scoped per workspace; the fingerprint covers the request method, path, and raw body."
          required: false
          name: X-Idempotency-Key
          in: header
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CategoryCreateRequest"
            examples:
              root:
                summary: Root category
                value:
                  company_id: 11111111-1111-1111-1111-111111111111
                  name: Despesas Operacionais
                  type: debit
              child:
                summary: Subcategory under a debit parent
                value:
                  company_id: 11111111-1111-1111-1111-111111111111
                  name: Aluguel
                  type: debit
                  parent_id: f8d2a5e1-3c47-4b90-9a1b-2c8f7e3d4b95
      responses:
        "201":
          description: Category created. The `Location` header points at the new row.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/CategoryV1"
                required:
                  - data
        "400":
          description: Malformed JSON body.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.categories.write` scope.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "409":
          description: "`X-Idempotency-Key` reused with a different body."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: Schema validation failure; `company_id` or `parent_id` not in the tenant; subcategory `type` not matching the parent; or tree depth > 3.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
  /categories/{id}:
    get:
      operationId: getCategory
      summary: Obter categoria por ID
      description: " Returns the same shape as the rows from the list endpoint. Requires scope `finance.categories`. 404 covers both \"doesn't exist\" and \"not in the tenant\" — the API does not distinguish."
      tags:
        - Categorias
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
          required: true
          name: id
          in: path
        - schema:
            type: string
            description: Comma-separated top-level fields to return, e.g. `fields=description,amount,due_date`. Omit for every field. `id` is always included; naming an object field returns it whole. An unknown name is a 400 whose `details` lists `unknown` and `available`.
            example: description,amount,due_date
          required: false
          name: fields
          in: query
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: Get a category by id
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/CategoryV1"
                required:
                  - data
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.categories` scope, or `sub` does not match any workspace.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Category not found within the tenant.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
    patch:
      operationId: updateCategory
      summary: Atualizar categoria
      description: Partial update of `name` and `type` only. Requires scope `finance.categories.write`. Re-parenting is not supported via v1. Changing a parent's `type` cascades to all descendants (internal behavior).
      tags:
        - Categorias
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
          required: true
          name: id
          in: path
        - schema:
            type: string
            description: "Opaque idempotency token. Same key + same body replays the cached response with `X-Idempotent-Replay: true`; same key + different body returns 409. Scoped per workspace; the fingerprint covers the request method, path, and raw body."
          required: false
          name: X-Idempotency-Key
          in: header
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CategoryUpdateRequest"
            examples:
              rename:
                summary: Rename
                value:
                  name: Aluguel e Condomínio
      responses:
        "200":
          description: Updated category.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/CategoryV1"
                required:
                  - data
        "400":
          description: Malformed JSON body.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.categories.write` scope.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Category not found within the tenant.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "409":
          description: "`X-Idempotency-Key` reused with a different body."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: Schema validation failure.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
    delete:
      operationId: deleteCategory
      summary: Excluir categoria
      description: "Deletes a category. Requires scope `finance.categories.write`. Returns 422 (`reason: has_children`) while the category still has subcategories — delete the children first. For a leaf, references on transactions / payables / receivables are set to null and the row is removed. Calling DELETE twice returns 200 then 404."
      tags:
        - Categorias
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
          required: true
          name: id
          in: path
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: Category deleted.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DeletedResponse"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.categories.write` scope.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Category not found within the tenant.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: "The category still has subcategories (`reason: has_children`)."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
  /companies:
    get:
      operationId: listCompanies
      summary: Listar empresas
      description: Returns a cursor-paginated page of companies for the workspace tied to the token's `sub` claim. Requires scope `finance.companies`. Catalogal entity — defaults to alphabetical sort by `name`.
      tags:
        - Empresas
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            enum:
              - "true"
              - "false"
            description: Filter by active flag. `true` returns only active; omit to return both.
          required: false
          name: active
          in: query
        - schema:
            type: string
            enum:
              - matriz
              - filial
            description: "`matriz` (head office) or `filial` (branch). Accepts `[in]=matriz,filial`."
          required: false
          name: type
          in: query
        - schema:
            type: string
            description: Filter by CNPJ (14-digit numeric string, no formatting). Accepts `[in]=cnpj1,cnpj2`.
            example: "12345678000190"
          required: false
          name: cnpj
          in: query
        - schema:
            type: string
            description: Filter by exact name. Accepts `[in]`.
            example: ACME Comércio Ltda
          required: false
          name: name
          in: query
        - schema:
            type: string
            enum:
              - created_at
              - name
            description: "Field to order by. Default: `sort_by=name&sort_order=asc`."
          required: false
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: Order direction. Defaults to `asc`; ignored without `sort_by`.
          required: false
          name: sort_order
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            description: Page size. Default 25, max 100.
            example: 25
          required: false
          name: limit
          in: query
        - schema:
            type: string
            description: Opaque cursor for the next page. Round-trip `meta.next_cursor` from the previous response.
          required: false
          name: cursor
          in: query
        - schema:
            type: string
            description: Comma-separated top-level fields to return, e.g. `fields=description,amount,due_date`. Omit for every field. `id` is always included; naming an object field returns it whole. An unknown name is a 400 whose `details` lists `unknown` and `available`.
            example: description,amount,due_date
          required: false
          name: fields
          in: query
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: Page of companies plus pagination metadata.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CompanyListResponse"
        "400":
          description: Malformed query (unknown filter / operator, invalid limit, bad cursor).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token authenticated but lacks the `finance.companies` scope, or `sub` does not match any workspace.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
    post:
      operationId: createCompany
      summary: Criar empresa
      description: Creates a company. Requires scope `finance.companies.write`. **`cnpj` is required and validated** with the standard BR check-digit algorithm — a missing, invalid, or document-less value returns 422 (there is no pessoa-física / document-less company in this API; a migrating client whose source row has no CNPJ must supply one). The CNPJ must be unique among active companies in the workspace (duplicate → 409). `type` (matriz/filial) is derived server-side from the CNPJ branch digits and is never read from the request. `active` starts `true`. Pass an `X-Idempotency-Key` header on retries.
      tags:
        - Empresas
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            description: "Opaque idempotency token. Same key + same body replays the cached response with `X-Idempotent-Replay: true`; same key + different body returns 409. Scoped per workspace; the fingerprint covers the request method, path, and raw body."
          required: false
          name: X-Idempotency-Key
          in: header
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CompanyCreateRequest"
            examples:
              matriz:
                summary: Head office (matriz)
                value:
                  name: ACME Comércio Ltda
                  cnpj: "12345678000190"
              fiscal:
                summary: With the fiscal identity a fiscal registry requires
                value:
                  name: ACME Comércio Ltda
                  cnpj: "12345678000190"
                  trade_name: ACME
                  municipal_registration: "123456"
                  tax_regime: LucroPresumido
                  address:
                    street: Rua das Notas
                    number: "100"
                    complement: Sala 4
                    neighborhood: Centro
                    city: Londrina
                    city_ibge_code: "4113700"
                    state: PR
                    zip_code: "86010000"
              formatted:
                summary: Formatted CNPJ (accepted, stripped)
                value:
                  name: ACME Filial SP
                  cnpj: 12.345.678/0002-71
      responses:
        "201":
          description: Company created. The `Location` header points at the new row; `type` is derived from the CNPJ.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/CompanyV1"
                required:
                  - data
        "400":
          description: Malformed JSON body.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.companies.write` scope.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "409":
          description: A company with the same CNPJ already exists (active), or `X-Idempotency-Key` reuse with a different body.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: Schema validation failure, or a missing / invalid / document-less `cnpj`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
  /companies/{id}:
    get:
      operationId: getCompany
      summary: Obter empresa por ID
      description: " Returns the same shape as the rows from the list endpoint. Requires scope `finance.companies`. 404 covers both \"doesn't exist\" and \"not in the tenant\" — the API does not distinguish."
      tags:
        - Empresas
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
          required: true
          name: id
          in: path
        - schema:
            type: string
            description: Comma-separated top-level fields to return, e.g. `fields=description,amount,due_date`. Omit for every field. `id` is always included; naming an object field returns it whole. An unknown name is a 400 whose `details` lists `unknown` and `available`.
            example: description,amount,due_date
          required: false
          name: fields
          in: query
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: Get a company by id
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/CompanyV1"
                required:
                  - data
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.companies` scope, or `sub` does not match any workspace.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Company not found within the tenant.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
    patch:
      operationId: updateCompany
      summary: Atualizar empresa
      description: "Partial update of `name` and `active`. Requires scope `finance.companies.write`. The CNPJ (and the `type` it derives) is immutable via the public API — send `active: false` to soft-deactivate."
      tags:
        - Empresas
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
          required: true
          name: id
          in: path
        - schema:
            type: string
            description: "Opaque idempotency token. Same key + same body replays the cached response with `X-Idempotent-Replay: true`; same key + different body returns 409. Scoped per workspace; the fingerprint covers the request method, path, and raw body."
          required: false
          name: X-Idempotency-Key
          in: header
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CompanyUpdateRequest"
            examples:
              rename:
                summary: Rename
                value:
                  name: ACME Comércio S.A.
              deactivate:
                summary: Soft-deactivate
                value:
                  active: false
              fiscal:
                summary: Replace the fiscal address wholesale
                value:
                  tax_regime: LucroReal
                  address:
                    street: Rua das Notas
                    number: "200"
                    neighborhood: Centro
                    city: Londrina
                    city_ibge_code: "4113700"
                    state: PR
                    zip_code: "86010000"
              clearAddress:
                summary: Clear the fiscal address
                value:
                  address: null
      responses:
        "200":
          description: Updated company.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/CompanyV1"
                required:
                  - data
        "400":
          description: Malformed JSON body.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.companies.write` scope.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Company not found within the tenant.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "409":
          description: "`X-Idempotency-Key` reused with a different body."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: Schema validation failure.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
    delete:
      operationId: deleteCompany
      summary: Excluir empresa
      description: "Hard-deletes a company. Requires scope `finance.companies.write`. Blocked with 409 (`details` carries the per-table counts `{ transactions, payables, receivables, people }`) when the company still has financial records or active people — remove or reassign those first, or soft-deactivate via `PATCH { active: false }`. Config tables (categories, classification centers, financial accounts, soft-deleted people) cascade automatically. Calling DELETE twice returns 200 then 404."
      tags:
        - Empresas
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
          required: true
          name: id
          in: path
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: Company deleted.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DeletedResponse"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.companies.write` scope.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Company not found within the tenant.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "409":
          description: The company has financial records or active people (`details` carries the counts).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
  /classification-centers:
    get:
      operationId: listClassificationCenters
      summary: Listar centros de classificação
      description: Returns a cursor-paginated page of classification centers for the workspace tied to the token's `sub` claim. Company-scoped within the workspace — pass `?company_id=…` to narrow. Requires scope `finance.cost_centers`. **Naming:** the path is hyphenated like every other v1 resource (`/classification-centers`), but the scope is still `finance.cost_centers` — the table behind it is `cost_centers`, and the scope names the grant, not the URL.
      tags:
        - Centros de classificação
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Filter by owning company UUID. Accepts `[in]=uuid1,uuid2` for OR matching.
            example: 62057dab-0f98-4020-ae1e-2c0803251b1e
          required: false
          name: company_id
          in: query
        - schema:
            type: string
            enum:
              - "true"
              - "false"
            description: Filter by active flag. `true` returns only active; omit to return both.
          required: false
          name: active
          in: query
        - schema:
            type: string
            enum:
              - cost
              - revenue
              - both
            description: "`cost` (deducts), `revenue` (adds) or `both` (usable on either side). Accepts `[in]=cost,revenue,both`."
          required: false
          name: type
          in: query
        - schema:
            type: string
            description: Filter by exact name. Accepts `[in]`.
            example: Operacional
          required: false
          name: name
          in: query
        - schema:
            type: string
            enum:
              - created_at
              - name
            description: "Field to order by. Default: `sort_by=name&sort_order=asc`."
          required: false
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: Order direction. Defaults to `asc`; ignored without `sort_by`.
          required: false
          name: sort_order
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            description: Page size. Default 25, max 100.
            example: 25
          required: false
          name: limit
          in: query
        - schema:
            type: string
            description: Opaque cursor for the next page. Round-trip `meta.next_cursor` from the previous response.
          required: false
          name: cursor
          in: query
        - schema:
            type: string
            description: Comma-separated top-level fields to return, e.g. `fields=description,amount,due_date`. Omit for every field. `id` is always included; naming an object field returns it whole. An unknown name is a 400 whose `details` lists `unknown` and `available`.
            example: description,amount,due_date
          required: false
          name: fields
          in: query
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: Page of classification centers plus pagination metadata.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ClassificationCenterListResponse"
        "400":
          description: Malformed query (unknown filter / operator, invalid limit, bad cursor).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token authenticated but lacks the `finance.cost_centers` scope, or `sub` does not match any workspace.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
    post:
      operationId: createClassificationCenter
      summary: Criar centro de classificação
      description: Creates a classification center. Requires scope `finance.cost_centers.write`. `workspace_id` is taken from the token. `type` (`cost` / `revenue`) drives the rateio polarity. Pass an `X-Idempotency-Key` header on retries. **Naming:** the path is hyphenated like every other v1 resource (`/classification-centers`), but the scope is still `finance.cost_centers` — the table behind it is `cost_centers`, and the scope names the grant, not the URL.
      tags:
        - Centros de classificação
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            description: "Opaque idempotency token. Same key + same body replays the cached response with `X-Idempotent-Replay: true`; same key + different body returns 409. Scoped per workspace; the fingerprint covers the request method, path, and raw body."
          required: false
          name: X-Idempotency-Key
          in: header
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ClassificationCenterCreateRequest"
            examples:
              cost:
                summary: A classification center
                value:
                  company_id: 11111111-1111-1111-1111-111111111111
                  name: Operacional
                  type: cost
              revenue:
                summary: A revenue center with a description
                value:
                  company_id: 11111111-1111-1111-1111-111111111111
                  name: Comercial
                  type: revenue
                  description: Time de vendas
              both:
                summary: A center usable on both sides
                value:
                  company_id: 11111111-1111-1111-1111-111111111111
                  name: Projetos
                  type: both
                  description: Custos e receitas por projeto
      responses:
        "201":
          description: Classification center created. The `Location` header points at the new row.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/ClassificationCenterV1"
                required:
                  - data
        "400":
          description: Malformed JSON body.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.cost_centers.write` scope.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "409":
          description: "`X-Idempotency-Key` reused with a different body."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: Schema validation failure, or `company_id` not in the tenant.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
  /classification-centers/{id}:
    get:
      operationId: getClassificationCenter
      summary: Obter centro de classificação por ID
      description: " Returns the same shape as the rows from the list endpoint. Requires scope `finance.cost_centers`. 404 covers both \"doesn't exist\" and \"not in the tenant\" — the API does not distinguish. **Naming:** the path is hyphenated like every other v1 resource (`/classification-centers`), but the scope is still `finance.cost_centers` — the table behind it is `cost_centers`, and the scope names the grant, not the URL."
      tags:
        - Centros de classificação
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
          required: true
          name: id
          in: path
        - schema:
            type: string
            description: Comma-separated top-level fields to return, e.g. `fields=description,amount,due_date`. Omit for every field. `id` is always included; naming an object field returns it whole. An unknown name is a 400 whose `details` lists `unknown` and `available`.
            example: description,amount,due_date
          required: false
          name: fields
          in: query
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: Get a classification center by id
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/ClassificationCenterV1"
                required:
                  - data
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.cost_centers` scope, or `sub` does not match any workspace.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Classification center not found within the tenant.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
    patch:
      operationId: updateClassificationCenter
      summary: Atualizar centro de classificação
      description: "Partial update of `name`, `type`, `description`, `active`. Requires scope `finance.cost_centers.write`. Send `description: null` to clear it, or `active: false` to soft-deactivate (existing references are preserved). **Naming:** the path is hyphenated like every other v1 resource (`/classification-centers`), but the scope is still `finance.cost_centers` — the table behind it is `cost_centers`, and the scope names the grant, not the URL."
      tags:
        - Centros de classificação
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
          required: true
          name: id
          in: path
        - schema:
            type: string
            description: "Opaque idempotency token. Same key + same body replays the cached response with `X-Idempotent-Replay: true`; same key + different body returns 409. Scoped per workspace; the fingerprint covers the request method, path, and raw body."
          required: false
          name: X-Idempotency-Key
          in: header
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ClassificationCenterUpdateRequest"
            examples:
              deactivate:
                summary: Soft-deactivate
                value:
                  active: false
              rename:
                summary: Rename + describe
                value:
                  name: Operacional (matriz)
                  description: Centro principal
      responses:
        "200":
          description: Updated classification center.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/ClassificationCenterV1"
                required:
                  - data
        "400":
          description: Malformed JSON body.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.cost_centers.write` scope.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Classification center not found within the tenant.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "409":
          description: "`X-Idempotency-Key` reused with a different body."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: Schema validation failure.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
    delete:
      operationId: deleteClassificationCenter
      summary: Excluir centro de classificação
      description: "Deletes a classification center. Requires scope `finance.cost_centers.write`. Returns 422 (`reason: in_use`, with a `usage` breakdown) when the classification center is referenced by a rateio allocation, billing or transaction — soft-deactivate via `PATCH { active: false }` instead. Calling DELETE twice returns 200 then 404. **Naming:** the path is hyphenated like every other v1 resource (`/classification-centers`), but the scope is still `finance.cost_centers` — the table behind it is `cost_centers`, and the scope names the grant, not the URL."
      tags:
        - Centros de classificação
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
          required: true
          name: id
          in: path
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: Classification center deleted.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DeletedResponse"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.cost_centers.write` scope.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Classification center not found within the tenant.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: "The classification center is in use (`reason: in_use`)."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
  /custom-classifiers:
    get:
      operationId: listCustomClassifiers
      summary: Listar classificadores personalizados
      description: Returns a cursor-paginated page of custom classifiers (user-defined classification dimensions) for the workspace tied to the token's `sub` claim. Each dimension carries its `values[]` inline. Company-scoped — pass `?company_id=…` to narrow. Requires scope `finance.custom_classifiers`.
      tags:
        - Classificadores personalizados
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Filter by owning company UUID. Accepts `[in]=uuid1,uuid2` for OR matching.
            example: 62057dab-0f98-4020-ae1e-2c0803251b1e
          required: false
          name: company_id
          in: query
        - schema:
            type: string
            enum:
              - "true"
              - "false"
            description: Filter by active flag. `true` returns only active; omit to return both.
          required: false
          name: active
          in: query
        - schema:
            type: string
            description: Filter by exact name. Accepts `[in]`.
            example: Região
          required: false
          name: name
          in: query
        - schema:
            type: string
            enum:
              - created_at
              - name
            description: "Field to order by. Default: `sort_by=name&sort_order=asc`."
          required: false
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: Order direction. Defaults to `asc`; ignored without `sort_by`.
          required: false
          name: sort_order
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            description: Page size. Default 25, max 100.
            example: 25
          required: false
          name: limit
          in: query
        - schema:
            type: string
            description: Opaque cursor for the next page. Round-trip `meta.next_cursor` from the previous response.
          required: false
          name: cursor
          in: query
        - schema:
            type: string
            description: Comma-separated top-level fields to return, e.g. `fields=description,amount,due_date`. Omit for every field. `id` is always included; naming an object field returns it whole. An unknown name is a 400 whose `details` lists `unknown` and `available`.
            example: description,amount,due_date
          required: false
          name: fields
          in: query
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: Page of custom classifiers plus pagination metadata.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CustomClassifierListResponse"
        "400":
          description: Malformed query (unknown filter / operator, invalid limit, bad cursor).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.custom_classifiers` scope, or `sub` does not match any workspace.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
    post:
      operationId: createCustomClassifier
      summary: Criar classificador personalizado
      description: Creates a custom classifier dimension. Requires scope `finance.custom_classifiers.write`. `workspace_id` is taken from the token. Add values with `POST /custom-classifiers/{id}/values`. Pass an `X-Idempotency-Key` header on retries.
      tags:
        - Classificadores personalizados
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            description: "Opaque idempotency token. Same key + same body replays the cached response with `X-Idempotent-Replay: true`; same key + different body returns 409. Scoped per workspace."
          required: false
          name: X-Idempotency-Key
          in: header
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CustomClassifierCreateRequest"
            examples:
              dimension:
                summary: A classification dimension
                value:
                  company_id: 11111111-1111-1111-1111-111111111111
                  name: Região
                  field_type: dropdown
      responses:
        "201":
          description: Custom classifier created. The `Location` header points at the new row.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/CustomClassifierV1"
                required:
                  - data
        "400":
          description: Malformed JSON body.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.custom_classifiers.write` scope.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "409":
          description: "`X-Idempotency-Key` reused with a different body."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: Schema validation failure, or `company_id` not in the tenant.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
  /custom-classifiers/{id}:
    get:
      operationId: getCustomClassifier
      summary: Obter classificador personalizado por ID
      description: " Returns the same shape as the rows from the list endpoint. Requires scope `finance.custom_classifiers`. 404 covers both \"doesn't exist\" and \"not in the tenant\" — the API does not distinguish."
      tags:
        - Classificadores personalizados
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
          required: true
          name: id
          in: path
        - schema:
            type: string
            description: Comma-separated top-level fields to return, e.g. `fields=description,amount,due_date`. Omit for every field. `id` is always included; naming an object field returns it whole. An unknown name is a 400 whose `details` lists `unknown` and `available`.
            example: description,amount,due_date
          required: false
          name: fields
          in: query
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: Get a custom classifier by id
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/CustomClassifierV1"
                required:
                  - data
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.custom_classifiers` scope, or `sub` does not match any workspace.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Custom classifier not found within the tenant.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
    patch:
      operationId: updateCustomClassifier
      summary: Atualizar classificador personalizado
      description: "Partial update of `name`, `field_type`, `number_format`, `option_style`, `active`. Requires scope `finance.custom_classifiers.write`. Send `active: false` to soft-deactivate (existing assignments are preserved)."
      tags:
        - Classificadores personalizados
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
          required: true
          name: id
          in: path
        - schema:
            type: string
            description: "Opaque idempotency token. Same key + same body replays the cached response with `X-Idempotent-Replay: true`; same key + different body returns 409. Scoped per workspace; the fingerprint covers the request method, path, and raw body."
          required: false
          name: X-Idempotency-Key
          in: header
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CustomClassifierUpdateRequest"
            examples:
              rename:
                summary: Rename
                value:
                  name: Região comercial
              deactivate:
                summary: Soft-deactivate
                value:
                  active: false
      responses:
        "200":
          description: Updated custom classifier.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/CustomClassifierV1"
                required:
                  - data
        "400":
          description: Malformed JSON body.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.custom_classifiers.write` scope.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Custom classifier not found within the tenant.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "409":
          description: "`X-Idempotency-Key` reused with a different body."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: Schema validation failure.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
    delete:
      operationId: deleteCustomClassifier
      summary: Excluir classificador personalizado
      description: "Deletes a custom classifier and its values. Requires scope `finance.custom_classifiers.write`. Returns 422 (`reason: in_use`, with a `usage` breakdown) when any value is assigned to a payable / receivable / transaction — soft-deactivate via `PATCH { active: false }` instead. Calling DELETE twice returns 200 then 404."
      tags:
        - Classificadores personalizados
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
          required: true
          name: id
          in: path
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: Custom classifier deleted.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DeletedResponse"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.custom_classifiers.write` scope.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Custom classifier not found within the tenant.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: "The custom classifier is in use (`reason: in_use`)."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
  /custom-classifiers/{id}/values:
    post:
      operationId: createCustomClassifierValue
      summary: Adicionar valor a um classificador personalizado
      description: Adds a value to the dimension. Requires scope `finance.custom_classifiers.write`. Pass an `X-Idempotency-Key` header on retries.
      tags:
        - Classificadores personalizados
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
          required: true
          name: id
          in: path
        - schema:
            type: string
            description: Opaque idempotency token. Same key + same body replays the cached response; scoped per workspace.
          required: false
          name: X-Idempotency-Key
          in: header
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CustomClassifierValueCreateRequest"
            examples:
              value:
                summary: A value
                value:
                  value: Sudeste
                  color: "#2a78d6"
      responses:
        "201":
          description: Value created. The `Location` header points at the new value.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/CustomClassifierValueV1"
                required:
                  - data
        "400":
          description: Malformed JSON body.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.custom_classifiers.write` scope.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Parent custom classifier not found within the tenant.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "409":
          description: "`X-Idempotency-Key` reused with a different body."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: Schema validation failure.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
  /custom-classifiers/{id}/values/{valueId}:
    patch:
      operationId: updateCustomClassifierValue
      summary: Atualizar valor de classificador personalizado
      description: "Partial update of a value's `value`, `color`, `active`. Requires scope `finance.custom_classifiers.write`. Send `active: false` to soft-deactivate, or `color: null` to clear the swatch."
      tags:
        - Classificadores personalizados
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
          required: true
          name: id
          in: path
        - schema:
            type: string
            format: uuid
          required: true
          name: valueId
          in: path
        - schema:
            type: string
            description: "Opaque idempotency token. Same key + same body replays the cached response with `X-Idempotent-Replay: true`; same key + different body returns 409. Scoped per workspace; the fingerprint covers the request method, path, and raw body."
          required: false
          name: X-Idempotency-Key
          in: header
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CustomClassifierValueUpdateRequest"
            examples:
              rename:
                summary: Rename
                value:
                  value: Sudeste (SP)
              recolor:
                summary: Change color
                value:
                  color: "#009583"
              deactivate:
                summary: Soft-deactivate
                value:
                  active: false
      responses:
        "200":
          description: Updated value.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/CustomClassifierValueV1"
                required:
                  - data
        "400":
          description: Malformed JSON body.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.custom_classifiers.write` scope.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Value (or its parent dimension) not found within the tenant.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "409":
          description: "`X-Idempotency-Key` reused with a different body."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: Schema validation failure.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
    delete:
      operationId: deleteCustomClassifierValue
      summary: Excluir valor de classificador personalizado
      description: "Deletes a value. Requires scope `finance.custom_classifiers.write`. Returns 422 (`reason: in_use`) when the value is assigned to a payable / receivable / transaction — soft-deactivate via `PATCH { active: false }` instead."
      tags:
        - Classificadores personalizados
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
          required: true
          name: id
          in: path
        - schema:
            type: string
            format: uuid
          required: true
          name: valueId
          in: path
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: Value deleted.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DeletedResponse"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.custom_classifiers.write` scope.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Value (or its parent dimension) not found within the tenant.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: "The value is in use (`reason: in_use`)."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
  /fiscal-documents:
    get:
      operationId: listFiscalDocuments
      summary: Listar notas fiscais
      description: |-
        Returns a cursor-paginated page of the fiscal documents (NF-e / NFS-e) **received** against the workspace's companies, for the workspace tied to the token's `sub` claim. These notes are parked read-only — the app never emits them, so there is no create/cancel here. Default sort is `-issued_at`. Requires scope `finance.fiscal_documents`.

        ⚠️ `xml_download_url` points at the **attachments** resource, so fetching the XML bytes needs a token that also carries `finance.attachments`. A note can legitimately keep no XML (a `summary`-completeness note), in which case `xml_attachment_id` and `xml_download_url` are both `null`.
      tags:
        - Notas Fiscais
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Filter by the linked workspace company UUID. Accepts `[in]`.
          required: false
          name: company_id
          in: query
        - schema:
            type: string
            enum:
              - service
              - product
            description: "`service` (NFS-e) or `product` (NF-e). Accepts `[in]`."
          required: false
          name: kind
          in: query
        - schema:
            type: string
            enum:
              - authorized
              - cancelled
              - unknown
            description: Fiscal status. Accepts `[in]`.
          required: false
          name: situation
          in: query
        - schema:
            type: string
            enum:
              - nfe_io
              - xml_upload
              - sefaz
            description: How the note entered the workspace. Accepts `[in]`.
          required: false
          name: source
          in: query
        - schema:
            type: string
            description: Filter by the 44-digit access key (exact). Accepts `[in]`.
            example: "35250814200166000187550010000104211099887766"
          required: false
          name: access_key
          in: query
        - schema:
            type: string
            description: Filter by document number (exact). Accepts `[in]`.
            example: "10421"
          required: false
          name: number
          in: query
        - schema:
            type: string
            description: Filter by issuer CNPJ/CPF (digits only) — every note from a given supplier. Accepts `[in]`.
            example: "14200166000187"
          required: false
          name: issuer_document
          in: query
        - schema:
            type: string
            description: "ISO date filter on the issue date. Range operators: `[gte]`, `[lte]`, `[gt]`, `[lt]`."
          required: false
          name: issued_at
          in: query
        - schema:
            type: string
            description: ISO timestamp filter on when the note appeared in the workspace. Range operators supported.
          required: false
          name: created_at
          in: query
        - schema:
            type: string
            description: "ISO timestamp filter on the last write. Range operators supported. This is the incremental-sync window: poll `?updated_at[gte]=<end of your last poll>&sort_by=updated_at`. Deletions do NOT appear here."
          required: false
          name: updated_at
          in: query
        - schema:
            type: string
            enum:
              - created_at
              - issued_at
              - total_amount
              - updated_at
            description: "Field to order by. Default: `sort_by=issued_at&sort_order=desc`."
          required: false
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: Order direction. Defaults to `asc`; ignored without `sort_by`.
          required: false
          name: sort_order
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            description: Page size. Default 25, max 100.
          required: false
          name: limit
          in: query
        - schema:
            type: string
            description: Opaque cursor for the next page. Round-trip `meta.next_cursor` from the previous response.
          required: false
          name: cursor
          in: query
        - schema:
            type: string
            description: Comma-separated top-level fields to return, e.g. `fields=description,amount,due_date`. Omit for every field. `id` is always included; naming an object field returns it whole. An unknown name is a 400 whose `details` lists `unknown` and `available`.
            example: description,amount,due_date
          required: false
          name: fields
          in: query
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: Page of fiscal documents plus pagination metadata.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/FiscalDocumentListResponse"
        "400":
          description: Malformed query (unknown filter / operator, invalid limit, bad cursor).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token authenticated but lacks the `finance.fiscal_documents` scope, or `sub` does not match any workspace.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
  /fiscal-documents/{id}:
    get:
      operationId: getFiscalDocument
      summary: Obter nota fiscal por ID
      description: A single received fiscal document. Returns the same shape as the rows from the list endpoint. Requires scope `finance.fiscal_documents`. 404 covers both "doesn't exist" and "not in the tenant" — the API does not distinguish. The XML bytes are not served here — follow `xml_download_url`, which lives on the attachments resource and needs `finance.attachments`.
      tags:
        - Notas Fiscais
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
          required: true
          name: id
          in: path
        - schema:
            type: string
            description: Comma-separated top-level fields to return, e.g. `fields=description,amount,due_date`. Omit for every field. `id` is always included; naming an object field returns it whole. An unknown name is a 400 whose `details` lists `unknown` and `available`.
            example: description,amount,due_date
          required: false
          name: fields
          in: query
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: Get a fiscal document by id
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/FiscalDocumentV1"
                required:
                  - data
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.fiscal_documents` scope, or `sub` does not match any workspace.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Fiscal document not found within the tenant.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
  /payables/ids:
    get:
      operationId: listPayableIds
      summary: Listar IDs de contas a pagar
      description: "Every payable id the token can see. Requires scope `finance.payables`. Pairs with `?updated_at[gte]=` on the listing. The delta reports what changed; this reports what still exists. A delta can never report a disappearance — the row it would have to describe is gone — so an id that stops appearing here is the only signal that something left. Diff this set against your mirror and drop what is missing. Takes no filters: the sweep must cover exactly what the listing shows, and a filter applied here but not to your mirror would read as mass deletion. Note that deletion is not the only exit: a payable whose payment approval is rejected leaves the visible set while its row stays put, and it disappears from this sweep exactly as a deleted one would."
      tags:
        - Contas a Pagar
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            description: Page size. Default 1000, max 10000 — far above the listing's 100, because a row here is one UUID.
          required: false
          name: limit
          in: query
        - schema:
            type: string
            description: Opaque cursor from the previous page's `meta.next_cursor`.
          required: false
          name: cursor
          in: query
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: Live payable ids.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/IdSweepV1"
        "400":
          description: Malformed query. This endpoint takes no filters or sort — passing one is a 400.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.payables` scope, or `sub` does not match any workspace.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
  /receivables/ids:
    get:
      operationId: listReceivableIds
      summary: Listar IDs de contas a receber
      description: "Every receivable id the token can see. Requires scope `finance.receivables`. Pairs with `?updated_at[gte]=` on the listing. The delta reports what changed; this reports what still exists. A delta can never report a disappearance — the row it would have to describe is gone — so an id that stops appearing here is the only signal that something left. Diff this set against your mirror and drop what is missing. Takes no filters: the sweep must cover exactly what the listing shows, and a filter applied here but not to your mirror would read as mass deletion."
      tags:
        - Contas a Receber
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            description: Page size. Default 1000, max 10000 — far above the listing's 100, because a row here is one UUID.
          required: false
          name: limit
          in: query
        - schema:
            type: string
            description: Opaque cursor from the previous page's `meta.next_cursor`.
          required: false
          name: cursor
          in: query
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: Live receivable ids.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/IdSweepV1"
        "400":
          description: Malformed query. This endpoint takes no filters or sort — passing one is a 400.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.receivables` scope, or `sub` does not match any workspace.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
  /financial-transactions/ids:
    get:
      operationId: listTransactionIds
      summary: Listar IDs de transações
      description: "Every financial transaction id the token can see. Requires scope `finance.transactions`. Pairs with `?updated_at[gte]=` on the listing. The delta reports what changed; this reports what still exists. A delta can never report a disappearance — the row it would have to describe is gone — so an id that stops appearing here is the only signal that something left. Diff this set against your mirror and drop what is missing. Takes no filters: the sweep must cover exactly what the listing shows, and a filter applied here but not to your mirror would read as mass deletion. This is the largest resource in the API, which is what makes the pairing worth the trouble: re-paging the full history to find a handful of removals is exactly the cost the delta exists to avoid."
      tags:
        - Transações Financeiras
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            description: Page size. Default 1000, max 10000 — far above the listing's 100, because a row here is one UUID.
          required: false
          name: limit
          in: query
        - schema:
            type: string
            description: Opaque cursor from the previous page's `meta.next_cursor`.
          required: false
          name: cursor
          in: query
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: Live financial transaction ids.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/IdSweepV1"
        "400":
          description: Malformed query. This endpoint takes no filters or sort — passing one is a 400.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.transactions` scope, or `sub` does not match any workspace.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
  /people/ids:
    get:
      operationId: listPersonIds
      summary: Listar IDs de pessoas
      description: "Every person id the token can see. Requires scope `finance.people`. Pairs with `?updated_at[gte]=` on the listing. The delta reports what changed; this reports what still exists. A delta can never report a disappearance — the row it would have to describe is gone — so an id that stops appearing here is the only signal that something left. Diff this set against your mirror and drop what is missing. Takes no filters: the sweep must cover exactly what the listing shows, and a filter applied here but not to your mirror would read as mass deletion. People are soft-deleted, so a removed person's row survives internally — but it is filtered out of every v1 read, including this one, which makes its absence here the deletion signal."
      tags:
        - Pessoas
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            description: Page size. Default 1000, max 10000 — far above the listing's 100, because a row here is one UUID.
          required: false
          name: limit
          in: query
        - schema:
            type: string
            description: Opaque cursor from the previous page's `meta.next_cursor`.
          required: false
          name: cursor
          in: query
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: Live person ids.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/IdSweepV1"
        "400":
          description: Malformed query. This endpoint takes no filters or sort — passing one is a 400.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.people` scope, or `sub` does not match any workspace.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
  /payables:
    get:
      operationId: listPayables
      summary: Listar contas a pagar
      description: Returns a cursor-paginated page of payables (accounts payable) for the workspace tied to the token's `sub` claim. Each row includes nested entity references (`person`, `category`, `classification_center`, each `{id, name}`) and aggregate counts (`tax_charges_count`, `classification_center_allocations_count`, `attachments_count`) — full child collections live in the per-resource detail endpoints. Each row also carries the settlement amounts (`interest_amount`, `discount_amount`, `ticket_amount`) and a derived `nominal_amount = amount + interest - discount`. Status is the raw DB enum (`pending` / `paid` / `cancelled`); derive overdue client-side via `status === "pending" AND due_date < today`. Default sort is `due_date` ascending. Payables awaiting human approval (see the create endpoint) are **omitted** from this list until an operator approves them in the app; a bill is still retrievable by id via `GET /payables/{id}`. To **list the approval queue** instead, filter it explicitly — `?payment_approval=pending_approval` (read-only; there is no approve/reject action on this API). Each row carries its `payment_approval` state. Requires scope `finance.payables`.
      tags:
        - Contas a Pagar
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Filter by owning company UUID. Accepts `[in]`.
          required: false
          name: company_id
          in: query
        - schema:
            type: string
            enum:
              - pending
              - paid
              - cancelled
            description: Raw status. To find overdue payables, combine `status=pending&due_date[lt]=<today ISO>`. Accepts `[in]=pending,cancelled`.
          required: false
          name: status
          in: query
        - schema:
            type: string
            enum:
              - normal
              - recurring
              - installment
            description: Billing kind. Accepts `[in]`.
          required: false
          name: type
          in: query
        - schema:
            type: string
            format: uuid
            description: Filter by person UUID. Accepts `[in]`.
          required: false
          name: person_id
          in: query
        - schema:
            type: string
            format: uuid
            description: Filter by category UUID. Accepts `[in]`.
          required: false
          name: category_id
          in: query
        - schema:
            type: string
            format: uuid
            description: Filter by primary classification-center UUID (not the rateio). Accepts `[in]`.
          required: false
          name: classification_center_id
          in: query
        - schema:
            type: string
            format: uuid
            description: Filter by settlement account UUID. Accepts `[in]`.
          required: false
          name: financial_account_id
          in: query
        - schema:
            type: string
            enum:
              - bank_billet
              - utility
              - tribute
              - ted
              - pix
              - credit_card
              - debit_card
              - cash
              - check
              - direct_debit
              - other
              - none
            description: Payment method enum (`utility`/`tribute` are payable-only). Accepts `[in]`.
          required: false
          name: payment_method_kind
          in: query
        - schema:
            type: string
            enum:
              - pending_approval
              - partially_approved
              - creation_approved
              - creation_rejected
            description: Read-only view of the human payment-approval gate. By default the list hides gated bills; filter this explicitly to **list the approval queue** — e.g. `?payment_approval=pending_approval`. Accepts `[in]`. There is no approve/reject action on this API; that stays a human action in the app.
          required: false
          name: payment_approval
          in: query
        - schema:
            type: string
            description: "ISO date filter. Range operators: `[gte]`, `[lte]`, `[gt]`, `[lt]`. Example: `?due_date[gte]=2026-01-01&due_date[lte]=2026-12-31`."
          required: false
          name: due_date
          in: query
        - schema:
            type: string
            description: ISO date filter on the actual payment date. Range operators supported.
          required: false
          name: payment_date
          in: query
        - schema:
            type: string
            description: ISO date filter on the accounting period. Range operators supported.
          required: false
          name: competence_date
          in: query
        - schema:
            type: string
            description: "ISO timestamp filter on the last write. Range operators supported. This is the incremental-sync window: poll `?updated_at[gte]=<end of your last poll>&sort_by=updated_at` to fetch only what changed. Deletions do NOT appear here — a deleted payable has no row to return; reconcile them against `GET /v1/payables/ids`."
          required: false
          name: updated_at
          in: query
        - schema:
            type: string
            description: Decimal filter on amount (BRL). Range operators supported.
          required: false
          name: amount
          in: query
        - schema:
            type: string
            description: Filter by exact document number. Accepts `[in]`.
          required: false
          name: document_number
          in: query
        - schema:
            type: string
            enum:
              - "true"
              - "false"
            description: Filter by tax-relevance flag.
          required: false
          name: ir_relevant
          in: query
        - schema:
            type: string
            format: uuid
            description: Filter siblings of a recurring/installment series.
          required: false
          name: group_id
          in: query
        - schema:
            type: string
            enum:
              - amount
              - competence_date
              - created_at
              - due_date
              - payment_date
              - updated_at
            description: "Field to order by. Default: `sort_by=due_date&sort_order=asc`."
          required: false
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: Order direction. Defaults to `asc`; ignored without `sort_by`.
          required: false
          name: sort_order
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            description: Page size. Default 25, max 100.
          required: false
          name: limit
          in: query
        - schema:
            type: string
            description: Opaque cursor for the next page. Round-trip `meta.next_cursor` from the previous response.
          required: false
          name: cursor
          in: query
        - schema:
            type: string
            description: Comma-separated top-level fields to return, e.g. `fields=description,amount,due_date`. Omit for every field. `id` is always included; naming an object field returns it whole. An unknown name is a 400 whose `details` lists `unknown` and `available`.
            example: description,amount,due_date
          required: false
          name: fields
          in: query
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: Page of payables plus pagination metadata.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PayableListResponse"
              examples:
                twoRows:
                  summary: One pending + one paid, denormalised names and aggregate counts populated
                  value:
                    data:
                      - id: 8c8b4c4f-2e0e-4f6e-9b8a-2a1c2b3d4e5f
                        company_id: 11111111-1111-1111-1111-111111111111
                        description: Aluguel — escritório matriz
                        amount: 3200
                        interest_amount: null
                        discount_amount: null
                        ticket_amount: null
                        nominal_amount: 3200
                        due_date: 2026-06-05T00:00:00.000Z
                        payment_date: null
                        competence_date: 2026-06-01T00:00:00.000Z
                        type: recurring
                        status: pending
                        person:
                          id: 55555555-5555-5555-5555-555555555555
                          name: Imobiliária Central
                        category:
                          id: 66666666-6666-6666-6666-666666666666
                          name: Aluguel
                        classification_center: null
                        financial_account_id: null
                        expected_account_id: 22222222-2222-2222-2222-222222222222
                        payment_method_kind: bank_billet
                        operation_kind: payment
                        payment_approval: null
                        document: null
                        document_number: ALUG-2026-06
                        document_date: 2026-05-25T00:00:00.000Z
                        installment_number: null
                        total_installments: null
                        parcels_amount: null
                        group_id: 77777777-7777-7777-7777-777777777777
                        transaction_id: null
                        ir_relevant: false
                        notes: null
                        is_reconciled: false
                        tax_charges_count: 0
                        classification_center_allocations_count: 0
                        attachments_count: 1
                        custom_classifications_count: 0
                        metadata: null
                        custom_metadata:
                          erp_id: AP-2026-0042
                        created_via: api
                        created_via_other: null
                        created_by_email: maria@empresa.com.br
                        updated_via: dashboard
                        updated_via_other: null
                        updated_by_email: maria@empresa.com.br
                        created_at: 2026-05-01T08:00:00.000Z
                        updated_at: 2026-05-01T08:00:00.000Z
                      - id: 9d9b5d5f-3f1f-5f7f-ac9b-3b2c3c4d5e6f
                        company_id: 11111111-1111-1111-1111-111111111111
                        description: Conta de energia — abril
                        amount: 420.5
                        interest_amount: 5.5
                        discount_amount: null
                        ticket_amount: 426
                        nominal_amount: 426
                        due_date: 2026-05-10T00:00:00.000Z
                        payment_date: 2026-05-09T00:00:00.000Z
                        competence_date: 2026-04-01T00:00:00.000Z
                        type: normal
                        status: paid
                        person:
                          id: 88888888-8888-8888-8888-888888888888
                          name: ENEL Distribuição
                        category:
                          id: 99999999-9999-9999-9999-999999999999
                          name: Utilidades — Energia
                        classification_center:
                          id: aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa
                          name: Administrativo
                        financial_account_id: 22222222-2222-2222-2222-222222222222
                        expected_account_id: null
                        payment_method_kind: pix
                        operation_kind: transfer
                        payment_approval: null
                        document: null
                        document_number: ENEL-202604
                        document_date: 2026-04-25T00:00:00.000Z
                        installment_number: null
                        total_installments: null
                        parcels_amount: null
                        group_id: null
                        transaction_id: bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb
                        ir_relevant: false
                        notes: null
                        is_reconciled: true
                        tax_charges_count: 0
                        classification_center_allocations_count: 1
                        attachments_count: 0
                        custom_classifications_count: 0
                        metadata: null
                        custom_metadata: null
                        created_via: api
                        created_via_other: null
                        created_by_email: maria@empresa.com.br
                        updated_via: dashboard
                        updated_via_other: null
                        updated_by_email: maria@empresa.com.br
                        created_at: 2026-04-26T08:00:00.000Z
                        updated_at: 2026-05-09T10:00:00.000Z
                    meta:
                      next_cursor: eyJpZCI6IjlkOWI1ZDVmLTNmMWYtNWY3Zi1hYzliLTNiMmMzYzRkNWU2ZiJ9
        "400":
          description: Malformed query (unknown filter / operator, invalid limit, bad cursor).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token authenticated but lacks the `finance.payables` scope, or `sub` does not match any workspace.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
    post:
      operationId: createPayable
      summary: Criar conta a pagar
      description: "Creates a single payable (or an installment/recurring series). Requires scope `finance.payables.write`. `workspace_id` is always taken from the token. A person is required — send `person: {id}` or `person: {name}`. `{name}` (person / category / classification_center) is find-or-create; sending both `id` and `name` inside the same nested object returns 400. `classification_centers[]` (rateio) and `tax_charges[]` are accepted; the rateio Σ is validated against `amount`. When payment approval is enabled for the workspace (a per-workspace setting, on by default), the created payable enters an **approval queue**: it is returned here (with its id) but is **hidden from `GET /payables` until an operator approves it in the app** — fetch it by id meanwhile. With the setting off, the payable appears on `GET /payables` immediately. Pass an `X-Idempotency-Key` header on retries."
      tags:
        - Contas a Pagar
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            description: "Opaque idempotency token. Same key + same body replays the cached response with `X-Idempotent-Replay: true`; same key + different body returns 409. Scoped per workspace; the fingerprint covers the request method, path, and raw body."
          required: false
          name: X-Idempotency-Key
          in: header
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PayableCreateRequest"
            examples:
              byId:
                summary: Simple payable with explicit FKs
                value:
                  company_id: 11111111-1111-1111-1111-111111111111
                  description: Aluguel — escritório matriz
                  amount: 3200
                  due_date: 2026-07-05T00:00:00.000Z
                  person:
                    id: 55555555-5555-5555-5555-555555555555
                  category:
                    id: 66666666-6666-6666-6666-666666666666
                  payment_method_kind: bank_billet
              withReceiptEmails:
                summary: Register who gets the receipt (comprovante) by e-mail
                value:
                  company_id: 11111111-1111-1111-1111-111111111111
                  description: Aluguel — escritório matriz
                  amount: 3200
                  due_date: 2026-07-05T00:00:00.000Z
                  person:
                    id: 55555555-5555-5555-5555-555555555555
                  receipt_emails:
                    - label: Contabilidade
                      address: contabil@imobiliariacentral.com.br
                    - address: financeiro@imobiliariacentral.com.br
              withRateioAndTax:
                summary: Find-or-create person, 2-way rateio + a withholding charge
                value:
                  company_id: 11111111-1111-1111-1111-111111111111
                  description: Serviço de consultoria — NF 4567
                  amount: 10000
                  due_date: 2026-07-15T00:00:00.000Z
                  competence_date: 2026-07-01T00:00:00.000Z
                  person:
                    name: Consultoria Acme LTDA
                  category:
                    name: Serviços — Consultoria
                  classification_centers:
                    - classification_center:
                        id: aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa
                      percentage: 60
                    - classification_center:
                        id: cccccccc-cccc-cccc-cccc-cccccccccccc
                      percentage: 40
                  tax_charges:
                    - tax:
                        id: dddddddd-dddd-dddd-dddd-dddddddddddd
                      amount: 150
                      withholding: true
              boletoWithBarcode:
                summary: Register a boleto with its barcode (payment coordinate)
                value:
                  company_id: 11111111-1111-1111-1111-111111111111
                  description: Fornecedor — NF 8899
                  amount: 1840
                  due_date: 2026-07-20T00:00:00.000Z
                  person:
                    name: Distribuidora Beta
                  payment_method_kind: bank_billet
                  barcode: "23793380296000000000000000000000000000000000"
              tributeWithGuide:
                summary: Register a tribute (DARF) with structured guia data instead of a barcode
                value:
                  company_id: 11111111-1111-1111-1111-111111111111
                  description: DARF — IRPJ 2T2026
                  amount: 5200
                  due_date: 2026-07-31T00:00:00.000Z
                  person:
                    name: Receita Federal
                  payment_method_kind: tribute
                  tax_kind: darf
                  tax_data:
                    tax_code: "2362"
                    calculation_date: 2026-06-30
                    expire_at: 2026-07-31
                    gross_income_amount: 5200
      responses:
        "201":
          description: "Payable created. The `Location` header points at the new row. The body is the full detail shape (arrays + reconciliation). `X-Idempotent-Replay: true` marks a replayed response."
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/PayableDetailV1"
                required:
                  - data
        "400":
          description: Malformed JSON, or `id` and `name` sent together inside the same nested entity reference.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.payables.write` scope.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "409":
          description: "`X-Idempotency-Key` reused with a different body."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: "Schema validation failure, or domain violation: missing person, company / account not in tenant, account does not match company, or rateio Σ ≠ amount."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
  /payables/{id}:
    get:
      operationId: getPayable
      summary: Obter conta a pagar por ID
      description: "List-row shape plus the expanded child arrays the list only counts: `classification_center_allocations[]` (rateio) and `tax_charges[]`, plus the `reconciliation` group (`null` when not reconciled). Returns the same shape as the rows from the list endpoint. Requires scope `finance.payables`. 404 covers both \"doesn't exist\" and \"not in the tenant\" — the API does not distinguish."
      tags:
        - Contas a Pagar
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
          required: true
          name: id
          in: path
        - schema:
            type: string
            description: Comma-separated top-level fields to return, e.g. `fields=description,amount,due_date`. Omit for every field. `id` is always included; naming an object field returns it whole. An unknown name is a 400 whose `details` lists `unknown` and `available`.
            example: description,amount,due_date
          required: false
          name: fields
          in: query
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: Get a payable by id
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/PayableDetailV1"
                required:
                  - data
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.payables` scope, or `sub` does not match any workspace.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Payable not found within the tenant.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
    patch:
      operationId: updatePayable
      summary: Atualizar ou liquidar conta a pagar
      description: 'Partial update. Only fields present in the body are written; unknown / response-only fields (`id`, `created_at`, `transaction_id`, denorm names, counts, `is_reconciled`) are silently dropped. Requires scope `finance.payables.write`. **Settle**: send `status:"paid"` with `payment_date` and `financial_account_id` (or rely on a previously-set account) — a debit payment transaction is generated automatically. The settlement amounts (`interest_amount` / `discount_amount` / `ticket_amount`) are persisted on the row. Set `status` back to `pending` to revert, or `cancelled` to cancel. `draft` is never accepted. Settling generates a payment transaction, so pass an `X-Idempotency-Key` header on retries — without it a retried settle is a second movement in the ledger.'
      tags:
        - Contas a Pagar
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
          required: true
          name: id
          in: path
        - schema:
            type: string
            description: "Opaque idempotency token. Same key + same body replays the cached response with `X-Idempotent-Replay: true`; same key + different body returns 409. Scoped per workspace; the fingerprint covers the request method, path, and raw body."
          required: false
          name: X-Idempotency-Key
          in: header
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PayableUpdateRequest"
            examples:
              settle:
                summary: Settle (status → paid) with late-payment interest
                value:
                  status: paid
                  payment_date: 2026-07-04T00:00:00.000Z
                  financial_account_id: 22222222-2222-2222-2222-222222222222
                  interest_amount: 25
                  ticket_amount: 3425
              editFields:
                summary: Edit a couple of fields
                value:
                  description: Aluguel — matriz (reajustado)
                  amount: 3400
      responses:
        "200":
          description: Updated payable (full detail shape).
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/PayableDetailV1"
                required:
                  - data
        "400":
          description: Malformed JSON, or `id` and `name` sent together inside the same nested entity reference.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.payables.write` scope.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Payable not found within the tenant.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "409":
          description: "`X-Idempotency-Key` reused with a different body."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: "Schema validation failure, or domain violation: settling without an account, FK out of scope, or rateio Σ ≠ amount."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
    delete:
      operationId: deletePayable
      summary: Excluir conta a pagar
      description: Hard-deletes the payable. Requires scope `finance.payables.write`. Dissolves any reconciliation the payable is part of and removes the auto-created payment transaction (manually-linked transactions are preserved). Calling DELETE twice on the same id returns 200 then 404 (Stripe-style). Deletes a single row — series-aware delete is not yet exposed.
      tags:
        - Contas a Pagar
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
          required: true
          name: id
          in: path
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: Payable deleted.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DeletedResponse"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.payables.write` scope.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Payable not found within the tenant.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
  /payables/{id}/send-to-bank:
    post:
      operationId: dispatchPayable
      summary: Enviar conta a pagar ao banco
      description: Dispatches an approved payable to the bank through the Kobana gateway (creates the payment, then envelopes and approves it on the gateway side). Requires scope `finance.payables.dispatch` — deliberately **separate** from `finance.payables.write` (dispatching money is not editing a bill). `X-Idempotency-Key` is **required**. Only an **official** bill dispatches — one whose `payment_approval` is `null` or `creation_approved`; a bill still awaiting approval (`pending_approval` / `partially_approved`) or `creation_rejected` returns 422. The payable must also carry the payment coordinates for its method and a Kobana-mirrored source account. Returns the refreshed payable detail with the execution mirror (`kobana_status`, `kobana_payment_uid`) populated. **Note:** the Kobana sandbox `example_bank` does not execute outbound payments, so the full `confirmed` cycle is only observable against an environment that actually settles.
      tags:
        - Contas a Pagar
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
          required: true
          name: id
          in: path
        - schema:
            type: string
            description: "REQUIRED for this operation — a duplicate dispatch is duplicate money. Opaque token; same key + same body replays the cached response with `X-Idempotent-Replay: true`. A missing / blank key returns 400."
          required: true
          name: X-Idempotency-Key
          in: header
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: Dispatched. Body is the refreshed payable detail with the Kobana mirror populated.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/PayableDetailV1"
                required:
                  - data
        "400":
          description: Missing / blank `X-Idempotency-Key`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.payables.dispatch` scope.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Payable not found within the tenant.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "409":
          description: "`X-Idempotency-Key` reused with a different body."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: "Not dispatchable: bill not official (awaiting approval / rejected), missing payment coordinates, source account not mirrored in Kobana, or the Kobana integration is not connected."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
  /payables/{id}/cancel-dispatch:
    post:
      operationId: cancelPayableDispatch
      summary: Cancelar o envio ao banco
      description: Undoes a payable's bank dispatch on the Kobana side and mirrors the payment as `canceled` (the `kobana_*` mirror is kept, not cleared — the bill returns to the manual rail because `canceled` is terminal). Requires scope `finance.payables.dispatch` and a **required** `X-Idempotency-Key`. A bill that was never dispatched, or whose execution already reached a terminal state (`confirmed` / `canceled` / …), returns 422. Returns the refreshed payable detail.
      tags:
        - Contas a Pagar
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
          required: true
          name: id
          in: path
        - schema:
            type: string
            description: "REQUIRED for this operation — a duplicate dispatch is duplicate money. Opaque token; same key + same body replays the cached response with `X-Idempotent-Replay: true`. A missing / blank key returns 400."
          required: true
          name: X-Idempotency-Key
          in: header
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: Dispatch cancelled. Body is the refreshed payable detail (`kobana_status` = `canceled`).
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/PayableDetailV1"
                required:
                  - data
        "400":
          description: Missing / blank `X-Idempotency-Key`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.payables.dispatch` scope.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Payable not found within the tenant.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "409":
          description: "`X-Idempotency-Key` reused with a different body."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: "Not cancellable: the bill was never dispatched, or its execution already reached a terminal state."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
  /people:
    get:
      operationId: listPeople
      summary: Listar pessoas (clientes / fornecedores)
      description: Returns a cursor-paginated page of people for the workspace tied to the token's `sub` claim. Soft-deleted rows are never returned. Company-scoped within the workspace — `?company_id=…` narrows. Sensitive fields (bank/PIX info, addresses, custom_data, birthday) are intentionally excluded from this endpoint. Requires scope `finance.people`.
      tags:
        - Pessoas
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Filter by owning company UUID. Accepts `[in]=uuid1,uuid2` for OR matching.
            example: 62057dab-0f98-4020-ae1e-2c0803251b1e
          required: false
          name: company_id
          in: query
        - schema:
            type: string
            enum:
              - natural
              - juridical
            description: "`natural` (pessoa física) or `juridical` (pessoa jurídica). Accepts `[in]=natural,juridical`."
          required: false
          name: kind
          in: query
        - schema:
            type: string
            enum:
              - "true"
              - "false"
            description: Filter by `is_customer` flag.
          required: false
          name: is_customer
          in: query
        - schema:
            type: string
            enum:
              - "true"
              - "false"
            description: Filter by `is_supplier` flag.
          required: false
          name: is_supplier
          in: query
        - schema:
            type: string
            enum:
              - "true"
              - "false"
            description: Filter by `is_bank_manager` flag.
          required: false
          name: is_bank_manager
          in: query
        - schema:
            type: string
            enum:
              - "true"
              - "false"
            description: Filter by `is_team` flag.
          required: false
          name: is_team
          in: query
        - schema:
            type: string
            enum:
              - "true"
              - "false"
            description: Filter by active flag.
          required: false
          name: active
          in: query
        - schema:
            type: string
            description: Filter by CPF/CNPJ (numeric-only string, no formatting). Accepts `[in]=11111111111,22222222222` for batch lookup.
            example: "12345678901"
          required: false
          name: document_number
          in: query
        - schema:
            type: string
            description: Filter by exact name. Accepts `[in]`.
          required: false
          name: name
          in: query
        - schema:
            type: string
            description: "ISO timestamp filter on the last write. Range operators supported. This is the incremental-sync window: poll `?updated_at[gte]=<end of your last poll>&sort_by=updated_at` to fetch only what changed. Deletions do NOT appear here — a deleted person is filtered out of every v1 read; reconcile them against `GET /v1/people/ids`."
          required: false
          name: updated_at
          in: query
        - schema:
            type: string
            enum:
              - created_at
              - name
              - updated_at
            description: "Field to order by. Default: `sort_by=name&sort_order=asc`."
          required: false
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: Order direction. Defaults to `asc`; ignored without `sort_by`.
          required: false
          name: sort_order
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            description: Page size. Default 25, max 100.
            example: 25
          required: false
          name: limit
          in: query
        - schema:
            type: string
            description: Opaque cursor for the next page. Round-trip `meta.next_cursor` from the previous response.
          required: false
          name: cursor
          in: query
        - schema:
            type: string
            description: Comma-separated top-level fields to return, e.g. `fields=description,amount,due_date`. Omit for every field. `id` is always included; naming an object field returns it whole. An unknown name is a 400 whose `details` lists `unknown` and `available`.
            example: description,amount,due_date
          required: false
          name: fields
          in: query
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: Page of people plus pagination metadata.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PersonListResponse"
        "400":
          description: Malformed query (unknown filter / operator, invalid limit, bad cursor).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token authenticated but lacks the `finance.people` scope, or `sub` does not match any workspace.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
    post:
      operationId: createPerson
      summary: Criar pessoa (cliente / fornecedor)
      description: Creates a person. Requires scope `finance.people.write`. `workspace_id` is always taken from the token. Set `is_customer` / `is_supplier` to make the row referenceable by receivables / payables. `document_number` (CPF 11 digits / CNPJ 14 digits) is validated and stored numeric-only; `document_type` is inferred from the length when omitted. Contact details (`emails` / `phones`), addresses, and bank/PIX coordinates are not yet writable via v1. Pass an `X-Idempotency-Key` header on retries.
      tags:
        - Pessoas
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            description: "Opaque idempotency token. Same key + same body replays the cached response with `X-Idempotent-Replay: true`; same key + different body returns 409. Scoped per workspace; the fingerprint covers the request method, path, and raw body."
          required: false
          name: X-Idempotency-Key
          in: header
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PersonCreateRequest"
            examples:
              supplier:
                summary: Juridical supplier with a CNPJ
                value:
                  company_id: 11111111-1111-1111-1111-111111111111
                  name: ACME Comércio Ltda
                  kind: juridical
                  document_type: cnpj
                  document_number: 11.444.777/0001-61
                  is_supplier: true
              customer:
                summary: Natural customer (document_type inferred from length)
                value:
                  company_id: 11111111-1111-1111-1111-111111111111
                  name: João da Silva
                  kind: natural
                  document_number: "39053344705"
                  is_customer: true
      responses:
        "201":
          description: Person created. The `Location` header points at the new row.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/PersonV1"
                required:
                  - data
        "400":
          description: Malformed JSON body.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.people.write` scope.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "409":
          description: A person with the same `document_number` already exists for the company, or `X-Idempotency-Key` reuse with a different body.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: Schema validation failure, invalid CPF/CNPJ check digits, or `company_id` not in the tenant.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
  /people/{id}:
    get:
      operationId: getPerson
      summary: Obter pessoa por ID
      description: Soft-deleted rows return 404 — same `deleted_at IS NULL` filter as the listing. Returns the same shape as the rows from the list endpoint. Requires scope `finance.people`. 404 covers both "doesn't exist" and "not in the tenant" — the API does not distinguish.
      tags:
        - Pessoas
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
          required: true
          name: id
          in: path
        - schema:
            type: string
            description: Comma-separated top-level fields to return, e.g. `fields=description,amount,due_date`. Omit for every field. `id` is always included; naming an object field returns it whole. An unknown name is a 400 whose `details` lists `unknown` and `available`.
            example: description,amount,due_date
          required: false
          name: fields
          in: query
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: Get a person by id
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/PersonV1"
                required:
                  - data
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.people` scope, or `sub` does not match any workspace.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Person not found within the tenant (or soft-deleted).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
    patch:
      operationId: updatePerson
      summary: Atualizar pessoa
      description: Partial update. Only fields present in the body are written; non-writable fields (`emails`, `phones`, bank/PIX, `id`, `workspace_id`, `company_id`, `created_at`) are dropped. Requires scope `finance.people.write`. Sending `null` for an optional string field is a no-op (clearing to null is not supported via v1). The CPF/CNPJ is re-validated only when `document_number` / `document_type` actually changes — re-sending the stored value verbatim (even a legacy/invalid one) is accepted so unrelated edits aren't blocked. A `document_type` sent on its own is rejected with 422 when it contradicts the stored number's digit count (e.g. tagging an 11-digit number as `cnpj`).
      tags:
        - Pessoas
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
          required: true
          name: id
          in: path
        - schema:
            type: string
            description: "Opaque idempotency token. Same key + same body replays the cached response with `X-Idempotent-Replay: true`; same key + different body returns 409. Scoped per workspace; the fingerprint covers the request method, path, and raw body."
          required: false
          name: X-Idempotency-Key
          in: header
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PersonUpdateRequest"
            examples:
              promote:
                summary: Promote a supplier to also be a customer
                value:
                  is_customer: true
              rename:
                summary: Edit name + notes
                value:
                  name: ACME Comércio e Serviços Ltda
                  notes: Razão social atualizada
      responses:
        "200":
          description: Updated person.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/PersonV1"
                required:
                  - data
        "400":
          description: Malformed JSON body.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.people.write` scope.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Person not found within the tenant (or soft-deleted).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "409":
          description: "`X-Idempotency-Key` reused with a different body."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: Schema validation failure, invalid CPF/CNPJ check digits when the document is changed, or a type-only change that contradicts the stored number's digit count.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
    delete:
      operationId: deletePerson
      summary: Excluir pessoa
      description: Soft-deletes the person (sets `deleted_at`). Requires scope `finance.people.write`. Existing payables / receivables / transactions referencing the person stay valid — the row just stops surfacing in listings and lookups. Calling DELETE twice returns 200 then 404 (Stripe-style). Refused with 409 while a classification rule still references the person (as match criterion or set action) — edit or remove the rule first.
      tags:
        - Pessoas
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
          required: true
          name: id
          in: path
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: Person soft-deleted.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DeletedResponse"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.people.write` scope.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Person not found within the tenant (or already deleted).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "409":
          description: The person is still referenced by a classification rule (match or set) — edit/remove the rule before deleting.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
  /receipts:
    get:
      operationId: listReceipts
      summary: Listar comprovantes de pagamento
      description: |-
        Returns a cursor-paginated page of payment receipts (comprovantes) for the workspace tied to the token's `sub` claim — the proof that a payment left. A receipt is either **captured** (`source: "kobana"`, produced in the background when a payment dispatched to the bank was confirmed) or **uploaded** (`source: "upload"`, a file someone attached for a payment made outside the system). Only an uploaded one can be missing a `payable_id`. Default sort is `-paid_at`. Requires scope `finance.receipts`.

        ⚠️ `download_url` points at the **attachments** resource, so fetching the bytes needs a token that also carries `finance.attachments`. And a row can legitimately have no binary at all: when the origin offered only a format we do not store, the receipt is `available` and reachable through `document_url` instead.
      tags:
        - Comprovantes
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Filter by owning company UUID. Accepts `[in]`.
          required: false
          name: company_id
          in: query
        - schema:
            type: string
            format: uuid
            description: Filter by the bill the receipt proves. Accepts `[in]`.
          required: false
          name: payable_id
          in: query
        - schema:
            type: string
            format: uuid
            description: Filter by the account the money left from. Accepts `[in]`.
          required: false
          name: financial_account_id
          in: query
        - schema:
            type: string
            enum:
              - kobana
              - upload
            description: "`kobana` (captured automatically) or `upload` (sent by a person). Accepts `[in]`."
          required: false
          name: source
          in: query
        - schema:
            type: string
            enum:
              - pending
              - available
              - failed
            description: Capture state. `?status=failed` is the queue of receipts a human should look at; `?status=available` is everything ready to fetch. Accepts `[in]`.
          required: false
          name: status
          in: query
        - schema:
            type: string
            enum:
              - bank_billet
              - utility
              - tribute
              - ted
              - pix
              - credit_card
              - debit_card
              - cash
              - check
              - direct_debit
              - other
              - none
            description: How the payment settled. Accepts `[in]`.
          required: false
          name: payment_method_kind
          in: query
        - schema:
            type: string
            description: Filter by the gateway payment `uid` the receipt was captured from — how you go from your own gateway record to the proof here. Accepts `[in]`.
          required: false
          name: external_uid
          in: query
        - schema:
            type: string
            description: "ISO date filter on the payment date. Range operators: `[gte]`, `[lte]`, `[gt]`, `[lt]`."
          required: false
          name: paid_at
          in: query
        - schema:
            type: string
            description: ISO timestamp filter on when the receipt row appeared. Range operators supported.
          required: false
          name: created_at
          in: query
        - schema:
            type: string
            description: "ISO timestamp filter on the last write. Range operators supported. This is the incremental-sync window: poll `?updated_at[gte]=<end of your last poll>&sort_by=updated_at` to pick up captures that landed since. Deletions do NOT appear here."
          required: false
          name: updated_at
          in: query
        - schema:
            type: string
            description: Decimal filter on the amount paid (BRL). Range operators supported.
          required: false
          name: amount
          in: query
        - schema:
            type: string
            enum:
              - amount
              - created_at
              - paid_at
              - updated_at
            description: "Field to order by. Default: `sort_by=paid_at&sort_order=desc`."
          required: false
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: Order direction. Defaults to `asc`; ignored without `sort_by`.
          required: false
          name: sort_order
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            description: Page size. Default 25, max 100.
          required: false
          name: limit
          in: query
        - schema:
            type: string
            description: Opaque cursor for the next page. Round-trip `meta.next_cursor` from the previous response.
          required: false
          name: cursor
          in: query
        - schema:
            type: string
            description: Comma-separated top-level fields to return, e.g. `fields=description,amount,due_date`. Omit for every field. `id` is always included; naming an object field returns it whole. An unknown name is a 400 whose `details` lists `unknown` and `available`.
            example: description,amount,due_date
          required: false
          name: fields
          in: query
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: Page of receipts plus pagination metadata.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ReceiptListResponse"
              examples:
                twoRows:
                  summary: One captured receipt with a stored PDF + one manual upload with no bill linked
                  value:
                    data:
                      - id: 6f2c1a3e-1b4d-4f8a-9c2e-7d5b8a1c3e9f
                        company_id: 11111111-1111-1111-1111-111111111111
                        payable_id: 33333333-3333-3333-3333-333333333333
                        payable_status: paid
                        financial_account_id: 22222222-2222-2222-2222-222222222222
                        source: kobana
                        status: available
                        paid_at: 2026-07-30T00:00:00.000Z
                        amount: 1500
                        payee_name: Companhia de Energia
                        payee_document: "12345678000190"
                        payment_method_kind: bank_billet
                        external_uid: pay_01HZX9V2K7QB
                        attachment_id: 8c8b4c4f-2e0e-4f6e-9b8a-2a1c2b3d4e5f
                        download_url: https://finance.kobana.example/api/v1/attachments/8c8b4c4f-2e0e-4f6e-9b8a-2a1c2b3d4e5f/download
                        document_url: https://kdoc.to/abc123.pdf
                        error_message: null
                        received_at: 2026-07-30T14:02:11.000Z
                        metadata: null
                        custom_metadata:
                          conferido_por: financeiro@empresa.com.br
                        created_via: api
                        created_via_other: null
                        created_by_email: maria@empresa.com.br
                        updated_via: dashboard
                        updated_via_other: null
                        updated_by_email: maria@empresa.com.br
                        created_at: 2026-07-30T14:02:11.000Z
                        updated_at: 2026-07-30T14:02:40.000Z
                      - id: 7a3d2b4f-2c5e-4a9b-8d3f-6e4c9b2d1a8e
                        company_id: 11111111-1111-1111-1111-111111111111
                        payable_id: null
                        payable_status: null
                        financial_account_id: null
                        source: upload
                        status: available
                        paid_at: 2026-07-28T00:00:00.000Z
                        amount: 320.9
                        payee_name: Prefeitura Municipal
                        payee_document: null
                        payment_method_kind: tribute
                        external_uid: null
                        attachment_id: 9d9b5d5f-3f1f-5f7f-ac9b-3b2c3c4d5e6f
                        download_url: https://finance.kobana.example/api/v1/attachments/9d9b5d5f-3f1f-5f7f-ac9b-3b2c3c4d5e6f/download
                        document_url: null
                        error_message: null
                        received_at: null
                        metadata: null
                        custom_metadata: null
                        created_via: api
                        created_via_other: null
                        created_by_email: maria@empresa.com.br
                        updated_via: dashboard
                        updated_via_other: null
                        updated_by_email: maria@empresa.com.br
                        created_at: 2026-07-29T09:12:00.000Z
                        updated_at: 2026-07-29T09:12:00.000Z
                    meta:
                      next_cursor: null
        "400":
          description: Malformed query (unknown filter / operator, invalid limit, bad cursor).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token authenticated but lacks the `finance.receipts` scope, or `sub` does not match any workspace.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
  /receipts/{id}:
    get:
      operationId: getReceipt
      summary: Obter comprovante de pagamento por ID
      description: A single payment receipt. Returns the same shape as the rows from the list endpoint. Requires scope `finance.receipts`. 404 covers both "doesn't exist" and "not in the tenant" — the API does not distinguish. The bytes are not served here — follow `download_url`, which lives on the attachments resource and needs `finance.attachments`.
      tags:
        - Comprovantes
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
          required: true
          name: id
          in: path
        - schema:
            type: string
            description: Comma-separated top-level fields to return, e.g. `fields=description,amount,due_date`. Omit for every field. `id` is always included; naming an object field returns it whole. An unknown name is a 400 whose `details` lists `unknown` and `available`.
            example: description,amount,due_date
          required: false
          name: fields
          in: query
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: Get a payment receipt by id
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/ReceiptV1"
                required:
                  - data
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.receipts` scope, or `sub` does not match any workspace.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Receipt not found within the tenant.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
  /receivables:
    get:
      operationId: listReceivables
      summary: Listar contas a receber
      description: "Returns a cursor-paginated page of receivables (accounts receivable) for the workspace tied to the token's `sub` claim. Mirror of `/payables` with the receive-side settlement amounts (`interest_amount`, `discount_amount`, `ticket_amount`) and a derived `nominal_amount = amount + interest - discount`. Status is the raw DB enum (`pending` / `received` / `cancelled`); derive overdue client-side. Every row also carries the **charge mirror** (`charge`) — whether a bank slip / Pix was issued for it and where that charge stands. `null` when never charged. Branch on `charge.state`, not on `charge.status`: the commercial axis alone calls a charge `opened` while its registration at the institution is still pending, and such a charge cannot be paid. Default sort is `due_date` ascending. Requires scope `finance.receivables`."
      tags:
        - Contas a Receber
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Filter by owning company UUID. Accepts `[in]`.
          required: false
          name: company_id
          in: query
        - schema:
            type: string
            enum:
              - pending
              - received
              - cancelled
            description: Raw status. To find overdue receivables, combine `status=pending&due_date[lt]=<today ISO>`. Accepts `[in]`.
          required: false
          name: status
          in: query
        - schema:
            type: string
            enum:
              - normal
              - recurring
              - installment
            description: Billing kind. Accepts `[in]`.
          required: false
          name: type
          in: query
        - schema:
            type: string
            format: uuid
            description: Filter by person UUID. Accepts `[in]`.
          required: false
          name: person_id
          in: query
        - schema:
            type: string
            format: uuid
            description: Filter by category UUID. Accepts `[in]`.
          required: false
          name: category_id
          in: query
        - schema:
            type: string
            format: uuid
            description: Filter by primary classification-center UUID. Accepts `[in]`.
          required: false
          name: classification_center_id
          in: query
        - schema:
            type: string
            format: uuid
            description: Filter by settlement account UUID. Accepts `[in]`.
          required: false
          name: financial_account_id
          in: query
        - schema:
            type: string
            enum:
              - bank_billet
              - ted
              - pix
              - credit_card
              - debit_card
              - cash
              - check
              - direct_debit
              - other
              - none
            description: Payment method enum. Accepts `[in]`.
          required: false
          name: payment_method_kind
          in: query
        - schema:
            type: string
            description: "ISO date filter. Range operators: `[gte]`, `[lte]`, `[gt]`, `[lt]`."
          required: false
          name: due_date
          in: query
        - schema:
            type: string
            description: ISO date filter on the actual receive date (column is `payment_date` to match the DB schema). Range operators supported.
          required: false
          name: payment_date
          in: query
        - schema:
            type: string
            description: ISO date filter on the accounting period. Range operators supported.
          required: false
          name: competence_date
          in: query
        - schema:
            type: string
            description: "ISO timestamp filter on the last write. Range operators supported. This is the incremental-sync window: poll `?updated_at[gte]=<end of your last poll>&sort_by=updated_at` to fetch only what changed. Deletions do NOT appear here — a deleted receivable has no row to return; reconcile them against `GET /v1/receivables/ids`."
          required: false
          name: updated_at
          in: query
        - schema:
            type: string
            description: Decimal filter on amount (BRL). Range operators supported.
          required: false
          name: amount
          in: query
        - schema:
            type: string
            description: Filter by exact document number. Accepts `[in]`.
          required: false
          name: document_number
          in: query
        - schema:
            type: string
            enum:
              - "true"
              - "false"
            description: Filter by tax-relevance flag.
          required: false
          name: ir_relevant
          in: query
        - schema:
            type: string
            format: uuid
            description: Filter siblings of a recurring/installment series.
          required: false
          name: group_id
          in: query
        - schema:
            type: string
            enum:
              - bank_billet
              - pix
            description: Which charge rail the receivable was issued on. Accepts `[in]`.
          required: false
          name: kobana_charge_kind
          in: query
        - schema:
            type: string
            description: Charge mirror, commercial axis (`opened`, `paid`, `overdue`, `canceled`, …). Free string — not validated against a fixed list, because the gateway owns the vocabulary. Accepts `[in]`.
          required: false
          name: kobana_status
          in: query
        - schema:
            type: string
            description: "Charge mirror, registration axis (`pending`, `confirmed`, `skipped`, `rejected`, `failed`). Free string. This is how you list the charges that can actually be delivered: `?kobana_registration_status=confirmed`. Accepts `[in]`."
          required: false
          name: kobana_registration_status
          in: query
        - schema:
            type: string
            enum:
              - amount
              - competence_date
              - created_at
              - due_date
              - payment_date
              - updated_at
            description: "Field to order by. Default: `sort_by=due_date&sort_order=asc`."
          required: false
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: Order direction. Defaults to `asc`; ignored without `sort_by`.
          required: false
          name: sort_order
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            description: Page size. Default 25, max 100.
          required: false
          name: limit
          in: query
        - schema:
            type: string
            description: Opaque cursor for the next page. Round-trip `meta.next_cursor` from the previous response.
          required: false
          name: cursor
          in: query
        - schema:
            type: string
            description: Comma-separated top-level fields to return, e.g. `fields=description,amount,due_date`. Omit for every field. `id` is always included; naming an object field returns it whole. An unknown name is a 400 whose `details` lists `unknown` and `available`.
            example: description,amount,due_date
          required: false
          name: fields
          in: query
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: Page of receivables plus pagination metadata.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ReceivableListResponse"
              examples:
                twoRows:
                  summary: One pending (bank slip registered) + one received (Pix paid) — note nominal_amount = amount + interest - discount
                  value:
                    data:
                      - id: 8c8b4c4f-2e0e-4f6e-9b8a-2a1c2b3d4e5f
                        company_id: 11111111-1111-1111-1111-111111111111
                        description: "NF #1234 — venda janeiro"
                        amount: 1500
                        interest_amount: null
                        discount_amount: null
                        ticket_amount: null
                        nominal_amount: 1500
                        due_date: 2026-06-10T00:00:00.000Z
                        payment_date: null
                        competence_date: 2026-05-01T00:00:00.000Z
                        type: normal
                        status: pending
                        person:
                          id: 44444444-4444-4444-4444-444444444444
                          name: Cliente XYZ Comércio LTDA
                        category: null
                        classification_center: null
                        financial_account_id: null
                        expected_account_id: 22222222-2222-2222-2222-222222222222
                        payment_method_kind: bank_billet
                        document: null
                        document_number: NF-1234
                        document_date: 2026-05-25T00:00:00.000Z
                        installment_number: null
                        total_installments: null
                        parcels_amount: null
                        group_id: null
                        transaction_id: null
                        ir_relevant: true
                        notes: null
                        is_reconciled: false
                        charge:
                          uid: chg_01HZX9V2K7QB
                          kind: bank_billet
                          status: opened
                          registration_status: confirmed
                          state: registered
                          status_updated_at: 2026-05-25T08:04:00.000Z
                        tax_charges_count: 2
                        classification_center_allocations_count: 0
                        attachments_count: 1
                        custom_classifications_count: 0
                        metadata: null
                        custom_metadata:
                          pedido: PED-8891
                        created_via: api
                        created_via_other: null
                        created_by_email: maria@empresa.com.br
                        updated_via: dashboard
                        updated_via_other: null
                        updated_by_email: maria@empresa.com.br
                        created_at: 2026-05-25T08:00:00.000Z
                        updated_at: 2026-05-25T08:00:00.000Z
                      - id: 9d9b5d5f-3f1f-5f7f-ac9b-3b2c3c4d5e6f
                        company_id: 11111111-1111-1111-1111-111111111111
                        description: "NF #1100 — consultoria abril"
                        amount: 2000
                        interest_amount: 25
                        discount_amount: null
                        ticket_amount: 2024.2
                        nominal_amount: 2025
                        due_date: 2026-04-30T00:00:00.000Z
                        payment_date: 2026-05-02T00:00:00.000Z
                        competence_date: 2026-04-01T00:00:00.000Z
                        type: normal
                        status: received
                        person:
                          id: 55555555-5555-5555-5555-555555555555
                          name: Consultoria ABC
                        category:
                          id: 66666666-6666-6666-6666-666666666666
                          name: Receita — Serviços
                        classification_center: null
                        financial_account_id: 22222222-2222-2222-2222-222222222222
                        expected_account_id: null
                        payment_method_kind: pix
                        document: null
                        document_number: NF-1100
                        document_date: 2026-04-20T00:00:00.000Z
                        installment_number: null
                        total_installments: null
                        parcels_amount: null
                        group_id: null
                        transaction_id: bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb
                        ir_relevant: true
                        notes: null
                        is_reconciled: true
                        charge:
                          uid: chg_01HZWQ4M8B3T
                          kind: pix
                          status: paid
                          registration_status: confirmed
                          state: paid
                          status_updated_at: 2026-05-02T14:00:00.000Z
                        tax_charges_count: 1
                        classification_center_allocations_count: 0
                        attachments_count: 0
                        custom_classifications_count: 0
                        metadata: null
                        custom_metadata: null
                        created_via: api
                        created_via_other: null
                        created_by_email: maria@empresa.com.br
                        updated_via: dashboard
                        updated_via_other: null
                        updated_by_email: maria@empresa.com.br
                        created_at: 2026-04-20T08:00:00.000Z
                        updated_at: 2026-05-02T14:00:00.000Z
                    meta:
                      next_cursor: null
        "400":
          description: Malformed query (unknown filter / operator, invalid limit, bad cursor).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token authenticated but lacks the `finance.receivables` scope, or `sub` does not match any workspace.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
    post:
      operationId: createReceivable
      summary: Criar conta a receber
      description: "Creates a single receivable (or an installment/recurring series). Requires scope `finance.receivables.write`. `workspace_id` is always taken from the token. A person is required — send `person: {id}` or `person: {name}`. `{name}` (person / category / classification_center) is find-or-create (customer/credit/revenue polarity); sending both `id` and `name` inside the same nested object returns 400. `classification_centers[]` (rateio) and `tax_charges[]` are accepted. Pass an `X-Idempotency-Key` header on retries."
      tags:
        - Contas a Receber
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            description: "Opaque idempotency token. Same key + same body replays the cached response with `X-Idempotent-Replay: true`; same key + different body returns 409. Scoped per workspace; the fingerprint covers the request method, path, and raw body."
          required: false
          name: X-Idempotency-Key
          in: header
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ReceivableCreateRequest"
            examples:
              byId:
                summary: Simple receivable with explicit FKs
                value:
                  company_id: 11111111-1111-1111-1111-111111111111
                  description: "NF #1234 — venda janeiro"
                  amount: 1500
                  due_date: 2026-07-10T00:00:00.000Z
                  person:
                    id: 44444444-4444-4444-4444-444444444444
                  payment_method_kind: bank_billet
              byName:
                summary: Find-or-create customer + a withholding charge
                value:
                  company_id: 11111111-1111-1111-1111-111111111111
                  description: Consultoria — abril
                  amount: 2000
                  due_date: 2026-07-15T00:00:00.000Z
                  person:
                    name: Cliente XYZ Comércio LTDA
                  category:
                    name: Receita — Serviços
                  tax_charges:
                    - tax:
                        id: dddddddd-dddd-dddd-dddd-dddddddddddd
                      amount: 30
      responses:
        "201":
          description: "Receivable created. The `Location` header points at the new row; the body is the full detail shape. `X-Idempotent-Replay: true` marks a replayed response."
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/ReceivableDetailV1"
                required:
                  - data
        "400":
          description: Malformed JSON, or `id` and `name` sent together inside the same nested entity reference.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.receivables.write` scope.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "409":
          description: "`X-Idempotency-Key` reused with a different body."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: "Schema validation failure, or domain violation: missing person, company / account not in tenant, account does not match company, or rateio Σ ≠ amount."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
  /receivables/{id}:
    get:
      operationId: getReceivable
      summary: Obter conta a receber por ID
      description: Mirror of the payable detail — denorm names + counts + `nominal_amount`, plus the expanded `classification_center_allocations[]` / `tax_charges[]` arrays and the `reconciliation` group (`null` when not reconciled). Returns the same shape as the rows from the list endpoint. Requires scope `finance.receivables`. 404 covers both "doesn't exist" and "not in the tenant" — the API does not distinguish. The detail's `charge` additionally carries what is handed to the payer when a charge was issued — `url`, `barcode`, `digitable_line`, `pix_qrcode` — while the list row's `charge` keeps only the state. All of them are `null` until the charge exists, and the Pix payload stays `null` until `state` reaches `registered`.
      tags:
        - Contas a Receber
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
          required: true
          name: id
          in: path
        - schema:
            type: string
            description: Comma-separated top-level fields to return, e.g. `fields=description,amount,due_date`. Omit for every field. `id` is always included; naming an object field returns it whole. An unknown name is a 400 whose `details` lists `unknown` and `available`.
            example: description,amount,due_date
          required: false
          name: fields
          in: query
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: Get a receivable by id
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/ReceivableDetailV1"
                required:
                  - data
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.receivables` scope, or `sub` does not match any workspace.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Receivable not found within the tenant.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
    patch:
      operationId: updateReceivable
      summary: Atualizar ou liquidar conta a receber
      description: 'Partial update. Only fields present in the body are written; unknown / response-only fields are silently dropped. Requires scope `finance.receivables.write`. **Settle (receive)**: send `status:"received"` with `payment_date` and `financial_account_id` (or a previously-set account) — an auto credit transaction is generated; the receive-time `interest_amount` / `discount_amount` / `ticket_amount` are persisted. Set `status` back to `pending` to revert, or `cancelled` to cancel. `draft` is never accepted. Settling generates a receipt transaction, so pass an `X-Idempotency-Key` header on retries — without it a retried settle is a second movement in the ledger.'
      tags:
        - Contas a Receber
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
          required: true
          name: id
          in: path
        - schema:
            type: string
            description: "Opaque idempotency token. Same key + same body replays the cached response with `X-Idempotent-Replay: true`; same key + different body returns 409. Scoped per workspace; the fingerprint covers the request method, path, and raw body."
          required: false
          name: X-Idempotency-Key
          in: header
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ReceivableUpdateRequest"
            examples:
              settle:
                summary: Receive with interest + ticket
                value:
                  status: received
                  payment_date: 2026-07-12T00:00:00.000Z
                  financial_account_id: 22222222-2222-2222-2222-222222222222
                  interest_amount: 25
                  ticket_amount: 1524.2
              editFields:
                summary: Edit a couple of fields
                value:
                  description: "NF #1234 (reemitida)"
                  amount: 1600
      responses:
        "200":
          description: Updated receivable (full detail shape).
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/ReceivableDetailV1"
                required:
                  - data
        "400":
          description: Malformed JSON, or `id` and `name` sent together inside the same nested entity reference.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.receivables.write` scope.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Receivable not found within the tenant.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "409":
          description: "`X-Idempotency-Key` reused with a different body."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: "Schema validation failure, or domain violation: receiving without an account, FK out of scope, or rateio Σ ≠ amount."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
    delete:
      operationId: deleteReceivable
      summary: Excluir conta a receber
      description: Hard-deletes the receivable. Requires scope `finance.receivables.write`. Dissolves any reconciliation and removes the auto-created receipt transaction (manually-linked transactions are preserved). Calling DELETE twice returns 200 then 404 (Stripe-style). Single-row — series-aware delete is not yet exposed.
      tags:
        - Contas a Receber
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
          required: true
          name: id
          in: path
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: Receivable deleted.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DeletedResponse"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.receivables.write` scope.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Receivable not found within the tenant.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
  /receivables/{id}/charge:
    post:
      operationId: issueReceivableCharge
      summary: Gerar cobrança de uma conta a receber
      description: |-
        Issues a **bank slip** or a **Pix charge** on the gateway so the customer has something to pay, and mirrors it onto the receivable. Requires scope `finance.receivables.charge` — a dedicated action scope, never bundled into `.write`, and the read god-scope `finance.all` does not satisfy it. An `X-Idempotency-Key` header is **required**: a duplicate charge is a second debt your customer receives.

        ⚠️ **The charge in the response is not deliverable yet.** Registration at the institution is asynchronous, so it comes back `charge.state: "registering"` — a bank slip does not exist at the bank yet and a Pix has no `charge.pix_qrcode`. Poll `GET /receivables/{id}` until `charge.state` is `registered` before sending anything to the payer. There is no refresh endpoint on purpose: the mirror is kept current by a background poll and by the gateway's webhook, and a per-call re-read would burn the same hourly request budget the rest of the integration shares.

        The two rails differ in what they require of the payer — a bank slip needs their **full address**, a Pix only name and document — and in what must be registered on the gateway account (an agreement vs. a Pix charging account). A missing piece comes back as 422 naming it; a state that forbids charging comes back as 409.
      tags:
        - Contas a Receber
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
          required: true
          name: id
          in: path
        - schema:
            type: string
            description: "**Required.** Same key + same body replays the recorded response with `X-Idempotent-Replay: true`; same key + different body returns 409."
          required: true
          name: X-Idempotency-Key
          in: header
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ReceivableChargeRequest"
            examples:
              pix:
                summary: Pix charge
                value:
                  kind: pix
              bankBillet:
                summary: Bank slip
                value:
                  kind: bank_billet
      responses:
        "200":
          description: Charge issued. The body is the refreshed receivable detail with the charge mirror populated (`charge.state` will read `registering`).
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/ReceivableDetailV1"
                required:
                  - data
        "400":
          description: Malformed JSON, or a missing / empty `X-Idempotency-Key`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.receivables.charge` scope.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Receivable not found within the tenant.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "409":
          description: "The receivable's state forbids charging: it already has a charge, or it was already received or cancelled. Also returned when `X-Idempotency-Key` is reused with a different body."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: "Something required is missing, and the message names it: no person linked, no CPF/CNPJ, no due date, no full address (bank slip only), or no charging account / agreement registered on the gateway account."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: The gateway refused the issue, or an unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
  /receivables/{id}/charge/cancel:
    post:
      operationId: cancelReceivableCharge
      summary: Cancelar a cobrança de uma conta a receber
      description: |-
        Cancels the charge on the gateway. Same guards as issuing it — Requires scope `finance.receivables.charge`, and an `X-Idempotency-Key` header is required.

        ⚠️ **A 200 does not mean the charge is dead.** A bank slip stops being payable at once, but a Pix cancellation is a *command* the financial institution confirms later — and until it does, your customer can still pay it. Read `charge.state` on the response: `canceled` is done; anything else means the cancellation is still in flight, and the background poll will keep the mirror moving.

        Cancelling is also how you unlock the bill: while a charge is live, `PATCH /receivables/{id}` refuses the fields that describe it (amount, due date, payer) and `DELETE` refuses the row — editing here would not re-issue anything, it would only make the record disagree with what the customer holds.
      tags:
        - Contas a Receber
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
          required: true
          name: id
          in: path
        - schema:
            type: string
            description: "**Required.** Same semantics as the issue endpoint."
          required: true
          name: X-Idempotency-Key
          in: header
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: Cancellation accepted. The body is the refreshed receivable detail — check `charge.state`.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/ReceivableDetailV1"
                required:
                  - data
        "400":
          description: Missing / empty `X-Idempotency-Key`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.receivables.charge` scope.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Receivable not found within the tenant.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "409":
          description: "Nothing to cancel: the receivable has no charge, or the charge already reached a closed state (paid, cancelled, failed). Also returned when `X-Idempotency-Key` is reused with a different body."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: The gateway refused the cancellation, or an unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
  /reconciliations:
    post:
      operationId: createReconciliation
      summary: Criar conciliação
      description: Reconciles billings against bank transactions (n:m), settling every billing (paid/received). Requires scope `finance.reconciliations.write`. All billings must share one polarity and one company; transaction polarity must match (payable↔debit, receivable↔credit). A billing already reconciled/linked, cancelled or draft is rejected (422). Pass an `X-Idempotency-Key` header on retries.
      tags:
        - Conciliações
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            description: Opaque idempotency token. Same key + same body replays the cached response; same key + different body returns 409.
          required: false
          name: X-Idempotency-Key
          in: header
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ReconciliationCreateRequest"
            examples:
              oneToOne:
                summary: Reconcile one payable with one debit transaction
                value:
                  billings:
                    - kind: payable
                      id: a1b2c3d4-0000-4000-8000-000000000001
                  transaction_ids:
                    - b1b2c3d4-0000-4000-8000-000000000002
      responses:
        "201":
          description: Reconciliation created; the `Location` header points at `GET /reconciliations/{id}`.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/ReconciliationV1"
                required:
                  - data
        "400":
          description: Malformed JSON body.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.reconciliations.write` scope.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: A billing or transaction was not found within the tenant.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "409":
          description: "`X-Idempotency-Key` reuse with a different body."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: "Validation failure: mixed polarity/company, wrong transaction polarity, a member already reconciled/linked, or a cancelled/draft billing."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
  /reconciliations/{id}:
    get:
      operationId: getReconciliation
      summary: Obter conciliação por ID
      description: Returns the reconciliation's light id-list shape (`source`, `kind`, `billing_ids`, `transaction_ids`). Requires scope `finance.reconciliations`. 404 when not in the tenant.
      tags:
        - Conciliações
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
          required: true
          name: id
          in: path
        - schema:
            type: string
            description: Comma-separated top-level fields to return, e.g. `fields=description,amount,due_date`. Omit for every field. `id` is always included; naming an object field returns it whole. An unknown name is a 400 whose `details` lists `unknown` and `available`.
            example: description,amount,due_date
          required: false
          name: fields
          in: query
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: The reconciliation.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/ReconciliationV1"
                required:
                  - data
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.reconciliations` scope, or `sub` does not match any workspace.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Reconciliation not found within the tenant.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
    delete:
      operationId: deleteReconciliation
      summary: Excluir conciliação
      description: "Dissolves a `manual` reconciliation: reverts every member billing to pending and clears the links. Requires scope `finance.reconciliations.write`. Imported (`fintera`) reconciliations are read-only → 422."
      tags:
        - Conciliações
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
          required: true
          name: id
          in: path
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: Reconciliation dissolved.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DeletedResponse"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.reconciliations.write` scope.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Reconciliation not found within the tenant.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: "The reconciliation is imported (`source: fintera`) and cannot be dissolved via the API."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
  /taxes:
    get:
      operationId: listTaxes
      summary: Listar impostos
      description: Returns a cursor-paginated page of tax kinds for the workspace tied to the token's `sub` claim. Requires scope `finance.taxes`. Catalogal entity — defaults to alphabetical sort by `name`.
      tags:
        - Impostos
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            enum:
              - "true"
              - "false"
            description: Filter by active flag. `true` returns only active (default UX preference); omit to return both.
          required: false
          name: active
          in: query
        - schema:
            type: string
            enum:
              - "true"
              - "false"
            description: Filter by `default_withholding` flag (whether new charges default to imposto retido).
          required: false
          name: default_withholding
          in: query
        - schema:
            type: string
            description: Filter by exact name (unique per workspace). Accepts `[in]=IRRF,INSS` for OR matching.
            example: IRRF
          required: false
          name: name
          in: query
        - schema:
            type: string
            enum:
              - created_at
              - name
            description: "Field to order by. Default: `sort_by=name&sort_order=asc`."
          required: false
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: Order direction. Defaults to `asc`; ignored without `sort_by`.
          required: false
          name: sort_order
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            description: Page size. Default 25, max 100.
            example: 25
          required: false
          name: limit
          in: query
        - schema:
            type: string
            description: Opaque cursor for the next page. Round-trip `meta.next_cursor` from the previous response.
          required: false
          name: cursor
          in: query
        - schema:
            type: string
            description: Comma-separated top-level fields to return, e.g. `fields=description,amount,due_date`. Omit for every field. `id` is always included; naming an object field returns it whole. An unknown name is a 400 whose `details` lists `unknown` and `available`.
            example: description,amount,due_date
          required: false
          name: fields
          in: query
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: Page of tax kinds plus pagination metadata.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TaxListResponse"
        "400":
          description: Malformed query (unknown filter / operator, invalid limit, bad cursor).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token authenticated but lacks the `finance.taxes` scope, or `sub` does not match any workspace.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
    post:
      operationId: createTax
      summary: Criar imposto
      description: Creates a tax kind. Requires scope `finance.taxes.write`. Workspace-scoped (no company). `name` is unique per workspace (duplicate → 409). `default_rate` is a percent string (0–100). Pass an `X-Idempotency-Key` header on retries.
      tags:
        - Impostos
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            description: "Opaque idempotency token. Same key + same body replays the cached response with `X-Idempotent-Replay: true`; same key + different body returns 409. Scoped per workspace; the fingerprint covers the request method, path, and raw body."
          required: false
          name: X-Idempotency-Key
          in: header
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TaxCreateRequest"
            examples:
              irrf:
                summary: IRRF (withheld) with a default rate
                value:
                  name: IRRF
                  default_rate: 1.5
                  default_withholding: true
              iss:
                summary: ISS (no default rate)
                value:
                  name: ISS
                  default_withholding: false
      responses:
        "201":
          description: Tax created. The `Location` header points at the new row.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/TaxV1"
                required:
                  - data
        "400":
          description: Malformed JSON body.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.taxes.write` scope.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "409":
          description: A tax with the same `name` already exists, or `X-Idempotency-Key` reuse with a different body.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: Schema validation failure, or `default_rate` out of the 0–100 range.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
  /taxes/{id}:
    get:
      operationId: getTax
      summary: Obter imposto por ID
      description: " Returns the same shape as the rows from the list endpoint. Requires scope `finance.taxes`. 404 covers both \"doesn't exist\" and \"not in the tenant\" — the API does not distinguish."
      tags:
        - Impostos
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
          required: true
          name: id
          in: path
        - schema:
            type: string
            description: Comma-separated top-level fields to return, e.g. `fields=description,amount,due_date`. Omit for every field. `id` is always included; naming an object field returns it whole. An unknown name is a 400 whose `details` lists `unknown` and `available`.
            example: description,amount,due_date
          required: false
          name: fields
          in: query
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: Get a tax kind by id
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/TaxV1"
                required:
                  - data
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.taxes` scope, or `sub` does not match any workspace.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Tax not found within the tenant.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
    patch:
      operationId: updateTax
      summary: Atualizar imposto
      description: "Partial update of `name`, `default_rate`, `default_withholding`, `active`. Requires scope `finance.taxes.write`. Send `default_rate: null` to clear it, or `active: false` to soft-deactivate."
      tags:
        - Impostos
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
          required: true
          name: id
          in: path
        - schema:
            type: string
            description: "Opaque idempotency token. Same key + same body replays the cached response with `X-Idempotent-Replay: true`; same key + different body returns 409. Scoped per workspace; the fingerprint covers the request method, path, and raw body."
          required: false
          name: X-Idempotency-Key
          in: header
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TaxUpdateRequest"
            examples:
              rate:
                summary: Adjust the default rate
                value:
                  default_rate: 1
              deactivate:
                summary: Soft-deactivate
                value:
                  active: false
      responses:
        "200":
          description: Updated tax.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/TaxV1"
                required:
                  - data
        "400":
          description: Malformed JSON body.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.taxes.write` scope.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Tax not found within the tenant.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "409":
          description: A tax with the same `name` already exists.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: Schema validation failure, or `default_rate` out of range.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
    delete:
      operationId: deleteTax
      summary: Excluir imposto
      description: "Deletes a tax. Requires scope `finance.taxes.write`. Returns 422 (`reason: in_use`, with the referencing payable/receivable ids) when the tax still has charges — soft-deactivate via `PATCH { active: false }` instead. Calling DELETE twice returns 200 then 404."
      tags:
        - Impostos
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
          required: true
          name: id
          in: path
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: Tax deleted.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DeletedResponse"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.taxes.write` scope.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Tax not found within the tenant.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: "The tax has charges (`reason: in_use`)."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
  /financial-transactions:
    get:
      operationId: listFinancialTransactions
      summary: Listar transações
      description: Returns a cursor-paginated page of transactions for the workspace tied to the token's `sub` claim. Requires scope `finance.transactions`.
      tags:
        - Transações Financeiras
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
            description: Filter by company UUID. Accepts `[in]=uuid1,uuid2` for OR matching.
            example: 11111111-1111-1111-1111-111111111111
          required: false
          name: company_id
          in: query
        - schema:
            type: string
            enum:
              - debit
              - credit
            description: "`debit` (money out) or `credit` (money in). Accepts `[in]=debit,credit`."
          required: false
          name: type
          in: query
        - schema:
            type: string
            format: uuid
            description: Filter by financial account UUID. Accepts `[in]`.
          required: false
          name: financial_account_id
          in: query
        - schema:
            type: string
            format: uuid
            description: Filter by person UUID. Accepts `[in]`.
          required: false
          name: person_id
          in: query
        - schema:
            type: string
            format: uuid
            description: Filter by category UUID. Accepts `[in]`.
          required: false
          name: category_id
          in: query
        - schema:
            type: string
            format: uuid
            description: Filter by classification-center UUID. Accepts `[in]`.
          required: false
          name: classification_center_id
          in: query
        - schema:
            type: string
            description: "ISO date filter on the movement date. Range operators supported: `[gte]`, `[lte]`, `[gt]`, `[lt]`. Example: `?occurred_at[gte]=2026-01-01&occurred_at[lte]=2026-12-31`."
            example: 2026-01-01
          required: false
          name: occurred_at
          in: query
        - schema:
            type: string
            description: "ISO timestamp filter on the last write. Range operators supported. This is the incremental-sync window: poll `?updated_at[gte]=<end of your last poll>&sort_by=updated_at` to fetch only what changed. Deletions do NOT appear here — a deleted transaction has no row to return; reconcile them against `GET /v1/financial-transactions/ids`."
          required: false
          name: updated_at
          in: query
        - schema:
            type: string
            description: "Decimal filter on amount (BRL). Range operators supported: `[gte]`, `[lte]`, `[gt]`, `[lt]`. A query parameter is always text, unlike the JSON body and the response, where money is a number."
            example: "100.00"
          required: false
          name: amount
          in: query
        - schema:
            type: string
            enum:
              - amount
              - created_at
              - occurred_at
              - updated_at
            description: "Field to order by. Default: `sort_by=occurred_at&sort_order=desc`."
          required: false
          name: sort_by
          in: query
        - schema:
            type: string
            enum:
              - asc
              - desc
            description: Order direction. Defaults to `asc`; ignored without `sort_by`.
          required: false
          name: sort_order
          in: query
        - schema:
            type: integer
            minimum: 1
            maximum: 100
            description: Page size. Default 25, max 100.
            example: 25
          required: false
          name: limit
          in: query
        - schema:
            type: string
            description: Opaque cursor for the next page. Round-trip `meta.next_cursor` from the previous response.
          required: false
          name: cursor
          in: query
        - schema:
            type: string
            description: Comma-separated top-level fields to return, e.g. `fields=description,amount,due_date`. Omit for every field. `id` is always included; naming an object field returns it whole. An unknown name is a 400 whose `details` lists `unknown` and `available`.
            example: description,amount,due_date
          required: false
          name: fields
          in: query
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: Page of transactions plus pagination metadata.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/FinancialTransactionListResponse"
              examples:
                twoRows:
                  summary: One debit + one credit, with a next-page cursor
                  value:
                    data:
                      - id: 8c8b4c4f-2e0e-4f6e-9b8a-2a1c2b3d4e5f
                        company_id: 11111111-1111-1111-1111-111111111111
                        financial_account_id: 22222222-2222-2222-2222-222222222222
                        description: Pagamento de fornecedor — NF 1234
                        bank_description: TED ACME LTDA
                        document: "1234"
                        document_date: 2026-05-20T00:00:00.000Z
                        amount: 1500
                        occurred_at: 2026-05-24T00:00:00.000Z
                        competence_date: 2026-05-01T00:00:00.000Z
                        type: debit
                        category:
                          id: 33333333-3333-3333-3333-333333333333
                        person:
                          id: 44444444-4444-4444-4444-444444444444
                        classification_center: null
                        classification_center_allocations: []
                        custom_classifications: []
                        payment_method_kind: ted
                        classified: true
                        notes: null
                        metadata: null
                        custom_metadata:
                          conciliado_em: 2026-05-25
                        created_via: api
                        created_via_other: null
                        created_by_email: maria@empresa.com.br
                        updated_via: dashboard
                        updated_via_other: null
                        updated_by_email: maria@empresa.com.br
                        created_at: 2026-05-24T10:32:18.412Z
                        updated_at: 2026-05-24T10:32:18.412Z
                      - id: 9d9b5d5f-3f1f-5f7f-ac9b-3b2c3c4d5e6f
                        company_id: 11111111-1111-1111-1111-111111111111
                        financial_account_id: 22222222-2222-2222-2222-222222222222
                        description: Recebimento cliente XYZ
                        bank_description: null
                        document: null
                        document_date: null
                        amount: 850
                        occurred_at: 2026-05-23T00:00:00.000Z
                        competence_date: 2026-05-01T00:00:00.000Z
                        type: credit
                        category: null
                        person: null
                        classification_center: null
                        classification_center_allocations: []
                        custom_classifications: []
                        payment_method_kind: pix
                        classified: false
                        notes: null
                        metadata: null
                        custom_metadata: null
                        created_via: api
                        created_via_other: null
                        created_by_email: maria@empresa.com.br
                        updated_via: dashboard
                        updated_via_other: null
                        updated_by_email: maria@empresa.com.br
                        created_at: 2026-05-23T14:05:00.000Z
                        updated_at: 2026-05-23T14:05:00.000Z
                    meta:
                      next_cursor: eyJpZCI6IjlkOWI1ZDVmLTNmMWYtNWY3Zi1hYzliLTNiMmMzYzRkNWU2ZiJ9
                empty:
                  summary: No matches — empty page
                  value:
                    data: []
                    meta:
                      next_cursor: null
        "400":
          description: Malformed query (unknown filter / operator, invalid limit, bad cursor).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
              examples:
                unknownFilter:
                  summary: Filter not in the allowlist
                  value:
                    error:
                      code: BAD_REQUEST
                      message: "Unknown filter: foo"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token authenticated but lacks the `finance.transactions` scope, or `sub` does not match any workspace.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
    post:
      operationId: createFinancialTransaction
      summary: Criar transação
      description: "Creates a single transaction. Requires scope `finance.transactions.write`. `workspace_id` is always taken from the token, never from the body. `auto_created` and `transfer_id` are always server-controlled — values in the request are ignored. `{name}` accepted as alternative to `{id}` inside the nested `person` / `category` / `classification_center` object, for CSV-importer ergonomics; sending both inside the same object returns 400. Send `classification_centers[]` to split the amount across classification centers (rateio): effective amounts must sum to `amount` (1-cent tolerance) and each center's polarity must match `type` (`both` matches either). When present it supersedes the single `classification_center` field (which then mirrors rows[0]); the full split rides back on every read as `classification_center_allocations[]`. Pass an `X-Idempotency-Key` header on retries to avoid duplicate inserts."
      tags:
        - Transações Financeiras
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            description: "Opaque idempotency token (recommended on POST). Same key + same body returns the cached response with `X-Idempotent-Replay: true`. Same key + different body returns 409 conflict. Scoped per workspace; the fingerprint covers the request method, path, and raw body."
          required: false
          name: X-Idempotency-Key
          in: header
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/FinancialTransactionCreateRequest"
            examples:
              byId:
                summary: Debit (saída) with explicit id references
                value:
                  company_id: 11111111-1111-1111-1111-111111111111
                  financial_account_id: 22222222-2222-2222-2222-222222222222
                  description: Pagamento de fornecedor — NF 1234
                  amount: 1500
                  occurred_at: 2026-05-24T00:00:00.000Z
                  type: debit
                  category:
                    id: 33333333-3333-3333-3333-333333333333
                  person:
                    id: 44444444-4444-4444-4444-444444444444
                  payment_method_kind: ted
              byName:
                summary: Credit (entrada) using {name} find-or-create
                value:
                  company_id: 11111111-1111-1111-1111-111111111111
                  financial_account_id: 22222222-2222-2222-2222-222222222222
                  description: Recebimento cliente XYZ
                  amount: 850
                  occurred_at: 2026-05-23T00:00:00.000Z
                  type: credit
                  category:
                    name: Vendas — Serviços
                  person:
                    name: XYZ Comércio LTDA
                  payment_method_kind: pix
              withRateio:
                summary: Debit split across two classification centers (rateio)
                value:
                  company_id: 11111111-1111-1111-1111-111111111111
                  financial_account_id: 22222222-2222-2222-2222-222222222222
                  description: Aluguel rateado entre centros
                  amount: 1000
                  occurred_at: 2026-05-24T00:00:00.000Z
                  type: debit
                  classification_centers:
                    - classification_center:
                        id: 55555555-5555-5555-5555-555555555555
                      percentage: 70
                    - classification_center:
                        id: 66666666-6666-6666-6666-666666666666
                      amount: 300
      responses:
        "201":
          description: "Transaction created. The `Location` header points at the new row. `X-Idempotent-Replay: true` indicates a cached response from a previous identical request."
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/FinancialTransactionV1"
                required:
                  - data
              examples:
                created:
                  summary: Created transaction (mirrors the request, plus server-set fields)
                  value:
                    data:
                      id: 8c8b4c4f-2e0e-4f6e-9b8a-2a1c2b3d4e5f
                      company_id: 11111111-1111-1111-1111-111111111111
                      financial_account_id: 22222222-2222-2222-2222-222222222222
                      description: Pagamento de fornecedor — NF 1234
                      bank_description: null
                      document: null
                      document_date: null
                      amount: 1500
                      occurred_at: 2026-05-24T00:00:00.000Z
                      competence_date: 2026-05-24T00:00:00.000Z
                      type: debit
                      category:
                        id: 33333333-3333-3333-3333-333333333333
                      person:
                        id: 44444444-4444-4444-4444-444444444444
                      classification_center: null
                      classification_center_allocations: []
                      custom_classifications: []
                      payment_method_kind: ted
                      classified: false
                      notes: null
                      metadata: null
                      custom_metadata: null
                      created_via: api
                      created_via_other: null
                      created_by_email: maria@empresa.com.br
                      updated_via: dashboard
                      updated_via_other: null
                      updated_by_email: maria@empresa.com.br
                      created_at: 2026-05-24T10:32:18.412Z
                      updated_at: 2026-05-24T10:32:18.412Z
                createdWithRateio:
                  summary: Created transaction with a two-center rateio (mirrors the withRateio request)
                  value:
                    data:
                      id: 7b7a3b3e-1d9d-3e5d-8a7f-1f0b1a2c3d4e
                      company_id: 11111111-1111-1111-1111-111111111111
                      financial_account_id: 22222222-2222-2222-2222-222222222222
                      description: Aluguel rateado entre centros
                      bank_description: null
                      document: null
                      document_date: null
                      amount: 1000
                      occurred_at: 2026-05-24T00:00:00.000Z
                      competence_date: 2026-05-24T00:00:00.000Z
                      type: debit
                      category: null
                      person: null
                      classification_center:
                        id: 55555555-5555-5555-5555-555555555555
                      classification_center_allocations:
                        - classification_center:
                            id: 55555555-5555-5555-5555-555555555555
                            name: Administrativo
                          percentage: 70
                          amount: 700
                        - classification_center:
                            id: 66666666-6666-6666-6666-666666666666
                            name: Comercial
                          percentage: null
                          amount: 300
                      custom_classifications: []
                      payment_method_kind: null
                      classified: false
                      notes: null
                      metadata: null
                      custom_metadata: null
                      created_via: api
                      created_via_other: null
                      created_by_email: maria@empresa.com.br
                      updated_via: dashboard
                      updated_via_other: null
                      updated_by_email: maria@empresa.com.br
                      created_at: 2026-05-24T10:32:18.412Z
                      updated_at: 2026-05-24T10:32:18.412Z
        "400":
          description: Malformed JSON body, or `id` and `name` sent together inside the same nested entity reference.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
              examples:
                mutualExclusion:
                  summary: Sent both category.id and category.name
                  value:
                    error:
                      code: BAD_REQUEST
                      message: Send "category.id" OR "category.name", not both.
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.transactions.write` scope.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
              examples:
                insufficientScope:
                  summary: Read-only token tried to write
                  value:
                    error:
                      code: INSUFFICIENT_SCOPE
                      message: Token is missing required scope.
                      details:
                        required: finance.transactions.write
                        granted:
                          - finance.transactions
        "409":
          description: "`X-Idempotency-Key` was reused with a different request body."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
              examples:
                idempotencyConflict:
                  summary: Same key, different body
                  value:
                    error:
                      code: CONFLICT
                      message: X-Idempotency-Key reused with a different request body.
        "422":
          description: Schema validation failure, or domain consistency violation (company / account not in tenant, account does not match company, FK out of scope).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
              examples:
                companyMismatch:
                  summary: Account does not belong to the given company
                  value:
                    error:
                      code: UNPROCESSABLE_ENTITY
                      message: financial_account does not belong to the given company.
                invalidAmount:
                  summary: Amount fails schema regex
                  value:
                    error:
                      code: UNPROCESSABLE_ENTITY
                      message: amount must be a number with at most 2 decimal places.
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
  /financial-transactions/{id}:
    patch:
      operationId: updateFinancialTransaction
      summary: Atualizar transação
      description: Partial update. Only fields present in the body are written. Requires scope `finance.transactions.write`. Read-only fields in the body (`id`, `workspace_id`, `company_id`, `financial_account_id`, `auto_created`, `transfer_id`, `created_at`) are silently dropped — the response is the new persisted state.
      tags:
        - Transações Financeiras
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
          required: true
          name: id
          in: path
        - schema:
            type: string
            description: "Opaque idempotency token. Same key + same body replays the cached response with `X-Idempotent-Replay: true`; same key + different body returns 409. Scoped per workspace; the fingerprint covers the request method, path, and raw body."
          required: false
          name: X-Idempotency-Key
          in: header
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                data:
                  $ref: "#/components/schemas/FinancialTransactionUpdateRequest"
              required:
                - data
      responses:
        "200":
          description: Updated transaction.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/FinancialTransactionV1"
                required:
                  - data
        "400":
          description: Malformed JSON body, or `id` and `name` sent together inside the same nested entity reference.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.transactions.write` scope.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Transaction not found within the tenant.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "409":
          description: "`X-Idempotency-Key` reused with a different body."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: Schema validation failure or FK out of scope.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
    delete:
      operationId: deleteFinancialTransaction
      summary: Excluir transação
      description: Hard-deletes the row. Requires scope `finance.transactions.write`. Deleting an auto-created row (one generated by paying / receiving a payable / receivable) returns 422 — cancel the source billing instead. Calling DELETE twice on the same id returns 200 then 404 (Stripe-style).
      tags:
        - Transações Financeiras
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
          required: true
          name: id
          in: path
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: Transaction deleted.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DeletedResponse"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.transactions.write` scope.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Transaction not found within the tenant.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: Auto-created transaction — cancel the parent payable / receivable instead.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
    get:
      operationId: getFinancialTransaction
      summary: Obter transação por ID
      description: " Returns the same shape as the rows from the list endpoint. Requires scope `finance.transactions`. 404 covers both \"doesn't exist\" and \"not in the tenant\" — the API does not distinguish."
      tags:
        - Transações Financeiras
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
          required: true
          name: id
          in: path
        - schema:
            type: string
            description: Comma-separated top-level fields to return, e.g. `fields=description,amount,due_date`. Omit for every field. `id` is always included; naming an object field returns it whole. An unknown name is a 400 whose `details` lists `unknown` and `available`.
            example: description,amount,due_date
          required: false
          name: fields
          in: query
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: Get a transaction by id
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/FinancialTransactionV1"
                required:
                  - data
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.transactions` scope, or `sub` does not match any workspace.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Transaction not found within the tenant.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
  /transfers:
    post:
      operationId: createTransfer
      summary: Criar transferência
      description: Creates an account-to-account transfer as a linked debit/credit pair sharing one `transfer_id`. Requires scope `finance.transactions.write`. The two accounts must differ, both must belong to the tenant, and both must have a linked company (cross-company is allowed). Pass an `X-Idempotency-Key` header on retries.
      tags:
        - Transferências
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            description: "Opaque idempotency token. Same key + same body replays the cached response with `X-Idempotent-Replay: true`; same key + different body returns 409."
          required: false
          name: X-Idempotency-Key
          in: header
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TransferCreateRequest"
            examples:
              basic:
                summary: Move R$ 1.500,00 between two accounts
                value:
                  from_account_id: a1b2c3d4-0000-4000-8000-000000000001
                  to_account_id: a1b2c3d4-0000-4000-8000-000000000002
                  amount: 1500
                  occurred_at: 2026-07-10T00:00:00.000Z
                  description: Aporte para conta pagamentos
      responses:
        "201":
          description: Transfer created. The `Location` header points at `GET /transfers/{transfer_id}`.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/TransferV1"
                required:
                  - data
        "400":
          description: Malformed JSON body.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.transactions.write` scope.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: A source/destination account was not found within the tenant.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "409":
          description: "`X-Idempotency-Key` reuse with a different body."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "422":
          description: Schema validation failure, same source/destination account, non-positive amount, or an account without a linked company.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
  /transfers/{id}:
    get:
      operationId: getTransfer
      summary: Obter transferência por ID
      description: Returns the linked debit/credit pair for a `transfer_id`. Requires scope `finance.transactions`. 404 when the transfer is not in the tenant.
      tags:
        - Transferências
      security:
        - bearerAuth: []
      parameters:
        - schema:
            type: string
            format: uuid
          required: true
          name: id
          in: path
        - schema:
            type: string
            description: Comma-separated top-level fields to return, e.g. `fields=description,amount,due_date`. Omit for every field. `id` is always included; naming an object field returns it whole. An unknown name is a 400 whose `details` lists `unknown` and `available`.
            example: description,amount,due_date
          required: false
          name: fields
          in: query
        - name: X-Workspace-Id
          in: header
          required: true
          description: Workspace to act on, as a UUID. The token identifies the organization; this header picks which of its workspaces the request reads or writes. It is checked against the token's organization, so a workspace owned by anyone else answers `UNKNOWN_WORKSPACE` — the same code as one that does not exist. Omitting it answers `WORKSPACE_REQUIRED` (400).
          schema:
            type: string
            format: uuid
            example: 2b93f876-769a-4bdc-b23e-304957f313b4
          example: 2b93f876-769a-4bdc-b23e-304957f313b4
        - name: User-Agent
          in: header
          required: true
          description: Identifies your integration. Include your name and contact email so the Kobana team can reach you if your integration causes issues (RFC 7231 §5.5.3).
          schema:
            type: string
            example: Kevin Mitnick <kmitnick@example.com>
          example: Kevin Mitnick <kmitnick@example.com>
        - name: X-Request-Source
          in: header
          required: false
          description: "Surface making the call: `dashboard`, `api`, `cli`, `mcp` or `other`. Recorded as the channel that created or last changed every record the request writes. Without the header, `api` is assumed; an unknown value answers `BAD_REQUEST` (400)."
          schema:
            type: string
            enum:
              - dashboard
              - api
              - cli
              - mcp
              - other
            default: api
        - name: X-Request-Source-Other
          in: header
          required: false
          description: Name of the app making the call. Required when `X-Request-Source` is `other` (up to 100 characters), ignored otherwise.
          schema:
            type: string
            maxLength: 100
            example: Zapier
          example: Zapier
      responses:
        "200":
          description: The transfer.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: "#/components/schemas/TransferV1"
                required:
                  - data
        "401":
          description: Missing / invalid / expired / revoked token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "403":
          description: Token lacks the `finance.transactions` scope, or `sub` does not match any workspace.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "404":
          description: Transfer not found within the tenant.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "429":
          description: Rate limit exceeded for this token. Reads and writes have separate budgets, both on a 1-hour sliding window. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` and `Retry-After` — wait out `Retry-After` rather than retrying immediately.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
        "500":
          description: Unhandled server error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiV1Error"
webhooks: {}
