{
  "openapi": "3.0.0",
  "paths": {
    "/api/v1/health": {
      "get": {
        "description": "Endpoint público. Não requer autenticação.",
        "operationId": "health",
        "parameters": [],
        "responses": {
          "200": {
            "description": "API está funcionando",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HealthDto"
                }
              }
            }
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Verificar saúde da API",
        "tags": [
          "Health"
        ]
      }
    },
    "/api/v1/documents/{id}/file": {
      "get": {
        "description": "Link assinado e temporário (`expiresInSeconds`, 10 minutos) para baixar o arquivo original. Peça um novo quando expirar. **Requires scope:** `documents:read`",
        "operationId": "getDocumentFile",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "ID do documento (UUID)",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DocumentFileUrlDto"
                }
              }
            }
          },
          "401": {
            "description": "Chave ausente, inválida ou revogada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "403": {
            "description": "Falta o escopo documents:read",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "404": {
            "description": "Não existe nesta conta",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "429": {
            "description": "Mais de 60 requisições por minuto",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Obter URL do arquivo do documento",
        "tags": [
          "Documents"
        ]
      }
    },
    "/api/v1/documents/{id}": {
      "get": {
        "description": "Situação do processamento, classificação, campos extraídos (achatados) e linhas de produto. Para o JSON de negócio estruturado use `GET /documents/{id}/data`. **Requires scope:** `documents:read`",
        "operationId": "getDocument",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "ID do documento (UUID)",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DocumentDetailDto"
                }
              }
            }
          },
          "401": {
            "description": "Chave ausente, inválida ou revogada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "403": {
            "description": "Falta o escopo documents:read",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "404": {
            "description": "Não existe nesta conta",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "429": {
            "description": "Mais de 60 requisições por minuto",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Buscar documento por ID",
        "tags": [
          "Documents"
        ]
      },
      "delete": {
        "description": "Exclui o documento, o arquivo e os dados extraídos. Não pode ser desfeito. **Requires scope:** `documents:delete`",
        "operationId": "deleteDocument",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "ID do documento (UUID)",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Excluído"
          },
          "401": {
            "description": "Chave ausente, inválida ou revogada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "403": {
            "description": "Falta o escopo documents:delete",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "404": {
            "description": "Não existe nesta conta",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "429": {
            "description": "Mais de 60 requisições por minuto",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Remover documento",
        "tags": [
          "Documents"
        ]
      }
    },
    "/api/v1/documents/{id}/data": {
      "get": {
        "description": "Devolve o documento como objeto de negócio, segundo um schema universal versionado — independente dos modelos de extração configurados pelo tenant.\n\n`coverage` diz o que esperar: `COVERED` (schema aplicado e completo), `PARTIAL` (schema aplicado, campos obrigatórios faltando — `missingRequired` os nomeia) `NOT_VERIFIED` (modelo ainda não conferido contra documento real; o JSON vem mesmo assim) ou `NOT_COVERED` (nenhum schema cobre este tipo documental; `data` vem nulo).\n\nAs chaves do JSON são as do MODELO classificado, em português, e cada parte (prestador, tomador, locador…) é um objeto próprio.\n\nValores monetários são **inteiros em centavos** com moeda. Campos que o documento oculta, não informa ou traz ilegíveis aparecem em `states`, nunca como zero. CPF, telefone e documentos vêm mascarados (CNPJ vem inteiro) e `maskedPaths` diz quais; `?reveal=true` exige o escopo `documents:read:sensitive`.\n\n**Requires scope:** `documents:read`",
        "operationId": "getDocumentData",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "ID do documento (UUID)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "includeEvidence",
            "required": false,
            "in": "query",
            "description": "Inclui a proveniência de cada caminho (fatos de origem).",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "includeDiagnostics",
            "required": false,
            "in": "query",
            "description": "Inclui os fatos não projetados e os findings.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "reveal",
            "required": false,
            "in": "query",
            "description": "Devolve valores sensíveis sem máscara. Exige o escopo documents:read:sensitive.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DocumentDataDto"
                }
              }
            }
          },
          "401": {
            "description": "Chave ausente, inválida ou revogada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "403": {
            "description": "Falta o escopo documents:read (ou documents:read:sensitive com reveal=true)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "404": {
            "description": "Não existe nesta conta",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "429": {
            "description": "Mais de 60 requisições por minuto",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Dados de negócio extraídos do documento",
        "tags": [
          "Documents"
        ]
      }
    },
    "/api/v1/documents": {
      "post": {
        "description": "Envia um arquivo para processamento: multipart (`file` + `name` + `type`) ou JSON com `metadata.fileBase64` e `metadata.mimeType` — no JSON, `name` precisa terminar na extensão (ex.: `nota.pdf`). Aceita PDF (até 200 páginas), PNG, JPG/JPEG, TIFF e WEBP, até 20 MB. O processamento é assíncrono: acompanhe por `GET /documents/{id}` até `COMPLETED` ou `FAILED`. Com o header `Idempotency-Key`, repetir o envio devolve o mesmo `id` com `duplicated: true`. Cobra créditos da carteira pelo processamento. **Requires scope:** `documents:write`",
        "operationId": "createDocument",
        "parameters": [
          {
            "name": "idempotency-key",
            "required": true,
            "in": "header",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "description": "Chave única por envio (ex.: id do seu sistema). Repetir a mesma chave não cria outro documento",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/CreateDocumentDto"
              }
            },
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateDocumentDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateDocumentResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Campo faltando (VALIDATION_ERROR) ou arquivo recusado (UPLOAD_EXTENSION_DENIED, UPLOAD_MIME_MISMATCH, UPLOAD_PDF_INVALID…)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationErrorResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Chave ausente, inválida ou revogada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "403": {
            "description": "Falta o escopo documents:write",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "413": {
            "description": "Arquivo acima de 20 MB (UPLOAD_TOO_LARGE)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "429": {
            "description": "Mais de 60 requisições por minuto",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Upload de documento",
        "tags": [
          "Documents"
        ]
      },
      "get": {
        "description": "Lista paginada, do mais recente para o mais antigo por padrão. `type` busca pelo nome do modelo classificado (parcial, sem caixa); `status` e `origin` aceitam só os valores listados (fora da lista = 400). **Requires scope:** `documents:read`",
        "operationId": "listDocuments",
        "parameters": [
          {
            "name": "page",
            "required": false,
            "in": "query",
            "schema": {
              "minimum": 1,
              "default": 1,
              "type": "number"
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "schema": {
              "minimum": 1,
              "maximum": 100,
              "default": 20,
              "type": "number"
            }
          },
          {
            "name": "sort",
            "required": false,
            "in": "query",
            "description": "Campo de ordenação",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "order",
            "required": false,
            "in": "query",
            "schema": {
              "default": "desc",
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ]
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "description": "Busca textual livre",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "createdAfter",
            "required": false,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "createdBefore",
            "required": false,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "updatedAfter",
            "required": false,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "updatedBefore",
            "required": false,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "required": false,
            "in": "query",
            "description": "Um ou mais status separados por vírgula: CREATED, UPLOADING, UPLOADED, PROCESSING, AWAITING_APPROVAL, COMPLETED, FAILED (ex.: COMPLETED,FAILED). Outro valor responde 400.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "type",
            "required": false,
            "in": "query",
            "description": "Parte do nome do modelo classificado, sem diferenciar maiúsculas (ex.: nfs-e, cnh)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "origin",
            "required": false,
            "in": "query",
            "description": "Canal de entrada: API, EMAIL, GMAIL, OUTLOOK, IMAP, DESKTOP_AGENT, MOBILE_APP, WHATSAPP, WEBHOOK, BULK_IMPORT, UNKNOWN… Outro valor responde 400.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "modelId",
            "required": false,
            "in": "query",
            "description": "Reservado: aceito, ainda não filtra a lista",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "workflow",
            "required": false,
            "in": "query",
            "description": "Reservado: aceito, ainda não filtra a lista",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DocumentListPageDto"
                }
              }
            }
          },
          "400": {
            "description": "Valor de filtro inválido",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationErrorResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Chave ausente, inválida ou revogada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "403": {
            "description": "Falta o escopo documents:read",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "429": {
            "description": "Mais de 60 requisições por minuto",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Listar documentos",
        "tags": [
          "Documents"
        ]
      }
    },
    "/api/v1/auth/me": {
      "get": {
        "description": "Retorna metadados da chave autenticada. O segredo nunca é retornado. **Requires scope:** `api_keys:read`",
        "operationId": "me",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Metadados da chave atual",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiKeyMeDto"
                }
              }
            }
          },
          "401": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "403": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Informações da API Key atual",
        "tags": [
          "Authentication"
        ]
      }
    },
    "/api/v1/auth/revoke": {
      "post": {
        "description": "Revoga permanentemente a chave usada nesta requisição. **Requires scope:** `api_keys:revoke`",
        "operationId": "revoke",
        "parameters": [],
        "responses": {
          "204": {
            "description": "Chave revogada"
          },
          "401": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "403": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Revogar a API Key atual",
        "tags": [
          "Authentication"
        ]
      }
    },
    "/api/v1/entities": {
      "get": {
        "description": "Pessoas, empresas e órgãos identificados nos documentos (deduplicados por CPF/CNPJ). CPF vem mascarado. **Requires scope:** `entities:read`",
        "operationId": "listEntities",
        "parameters": [
          {
            "name": "createdBefore",
            "required": false,
            "in": "query",
            "description": "Criadas até (ISO 8601). Aplicado sobre a página retornada",
            "schema": {}
          },
          {
            "name": "createdAfter",
            "required": false,
            "in": "query",
            "description": "Criadas a partir de (ISO 8601). Aplicado sobre a página retornada",
            "schema": {}
          },
          {
            "name": "type",
            "required": false,
            "in": "query",
            "description": "Tipo da entidade",
            "schema": {
              "enum": [
                "pf",
                "pj",
                "orgao"
              ],
              "type": "string"
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "description": "Parte do nome, CPF/CNPJ (só dígitos) ou tag",
            "schema": {}
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Itens por página (máx. 100)",
            "schema": {
              "example": 20,
              "type": "number"
            }
          },
          {
            "name": "page",
            "required": false,
            "in": "query",
            "description": "Página (começa em 1)",
            "schema": {
              "example": 1,
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EntityListPageDto"
                }
              }
            }
          },
          "401": {
            "description": "Chave ausente, inválida ou revogada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "403": {
            "description": "Falta o escopo entities:read",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "429": {
            "description": "Mais de 60 requisições por minuto",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Listar entidades extraídas",
        "tags": [
          "Entities"
        ]
      }
    },
    "/api/v1/entities/{id}": {
      "get": {
        "description": "Ficha da entidade com e-mails, papéis e os documentos em que aparece. **Requires scope:** `entities:read`",
        "operationId": "getEntity",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Id da entidade (de GET /entities)",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EntityDetailDto"
                }
              }
            }
          },
          "401": {
            "description": "Chave ausente, inválida ou revogada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "403": {
            "description": "Falta o escopo entities:read",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "404": {
            "description": "Não existe nesta conta",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "429": {
            "description": "Mais de 60 requisições por minuto",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Consultar entidade por ID",
        "tags": [
          "Entities"
        ]
      }
    },
    "/api/v1/products": {
      "get": {
        "description": "Catálogo de produtos montado a partir das notas fiscais de produto (NF-e), com estoque calculado pelas entradas e saídas. Valores em reais. Com `documentId`, devolve as linhas daquela nota. **Requires scope:** `products:read`",
        "operationId": "listProducts",
        "parameters": [
          {
            "name": "createdBefore",
            "required": false,
            "in": "query",
            "description": "Produtos com movimentação até esta data (ISO 8601)",
            "schema": {}
          },
          {
            "name": "createdAfter",
            "required": false,
            "in": "query",
            "description": "Produtos com movimentação a partir desta data (ISO 8601)",
            "schema": {}
          },
          {
            "name": "documentId",
            "required": false,
            "in": "query",
            "description": "Id de um documento: devolve as linhas de produto daquela nota",
            "schema": {}
          },
          {
            "name": "categoria",
            "required": false,
            "in": "query",
            "description": "Categoria exata",
            "schema": {}
          },
          {
            "name": "ncm",
            "required": false,
            "in": "query",
            "description": "NCM exato (só dígitos)",
            "schema": {
              "example": "61091000"
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "description": "Parte da descrição, código (SKU) ou NCM",
            "schema": {}
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Itens por página (máx. 100)",
            "schema": {
              "example": 20,
              "type": "number"
            }
          },
          {
            "name": "page",
            "required": false,
            "in": "query",
            "description": "Página (começa em 1)",
            "schema": {
              "example": 1,
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProductListPageDto"
                }
              }
            }
          },
          "401": {
            "description": "Chave ausente, inválida ou revogada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "403": {
            "description": "Falta o escopo products:read",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "429": {
            "description": "Mais de 60 requisições por minuto",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Listar produtos detectados",
        "tags": [
          "Products"
        ]
      }
    },
    "/api/v1/products/{id}": {
      "get": {
        "description": "Produto com estoque, códigos por fornecedor, movimentações (uma por linha de nota) e notas em que aparece. **Requires scope:** `products:read`",
        "operationId": "getProduct",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Id do produto (de GET /products)",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProductDetailDto"
                }
              }
            }
          },
          "401": {
            "description": "Chave ausente, inválida ou revogada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "403": {
            "description": "Falta o escopo products:read",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "404": {
            "description": "Não existe nesta conta",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "429": {
            "description": "Mais de 60 requisições por minuto",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Consultar produto por ID",
        "tags": [
          "Products"
        ]
      }
    },
    "/api/v1/document-models": {
      "get": {
        "description": "Modelos que a conta usa para classificar e extrair: os padrões do NouVy (`origin: SYSTEM`) e os criados na conta (`origin: TENANT`). Só `page`, `limit` e `search` (parte do nome ou da chave) têm efeito. **Requires scope:** `models:read`",
        "operationId": "listDocumentModels",
        "parameters": [
          {
            "name": "page",
            "required": false,
            "in": "query",
            "schema": {
              "minimum": 1,
              "default": 1,
              "type": "number"
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "schema": {
              "minimum": 1,
              "maximum": 100,
              "default": 20,
              "type": "number"
            }
          },
          {
            "name": "sort",
            "required": false,
            "in": "query",
            "description": "Campo de ordenação",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "order",
            "required": false,
            "in": "query",
            "schema": {
              "default": "desc",
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ]
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "description": "Busca textual livre",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "createdAfter",
            "required": false,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "createdBefore",
            "required": false,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "updatedAfter",
            "required": false,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "updatedBefore",
            "required": false,
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DocumentModelListPageDto"
                }
              }
            }
          },
          "401": {
            "description": "Chave ausente, inválida ou revogada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "403": {
            "description": "Falta o escopo models:read",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "429": {
            "description": "Mais de 60 requisições por minuto",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Listar modelos de documento",
        "tags": [
          "Document Models"
        ]
      },
      "post": {
        "description": "Cria um modelo na conta, vazio ou copiando os campos de um modelo do catálogo (`baseModelCatalogId`). Os campos e evidências são configurados no painel. **Requires scope:** `models:write`",
        "operationId": "createDocumentModel",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateDocumentModelBodyDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreatedDocumentModelDto"
                }
              }
            }
          },
          "400": {
            "description": "Nome vazio",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationErrorResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Chave ausente, inválida ou revogada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "403": {
            "description": "Falta o escopo models:write",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "404": {
            "description": "baseModelCatalogId não existe",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "409": {
            "description": "Já existe um modelo com esse nome (code CONFLICT)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "429": {
            "description": "Mais de 60 requisições por minuto",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Criar modelo customizado do tenant",
        "tags": [
          "Document Models"
        ]
      }
    },
    "/api/v1/document-models/{id}": {
      "get": {
        "description": "Modelo com os campos (`fields`) e o `workflowModelKey` — os valores para `modelKey`, `requiredFields` e regras ao montar um workflow. **Requires scope:** `models:read`",
        "operationId": "getDocumentModel",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Id do modelo, chave do modelo padrão (ex.: cnh) ou id do modelo da conta",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DocumentModelDetailDto"
                }
              }
            }
          },
          "401": {
            "description": "Chave ausente, inválida ou revogada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "403": {
            "description": "Falta o escopo models:read",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "404": {
            "description": "Não existe nesta conta",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "429": {
            "description": "Mais de 60 requisições por minuto",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Consultar modelo de documento",
        "tags": [
          "Document Models"
        ]
      },
      "patch": {
        "description": "Renomeia um modelo da conta. Modelo padrão responde 403. **Requires scope:** `models:write`",
        "operationId": "updateDocumentModel",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Id do modelo da conta",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateDocumentModelBodyDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DocumentModelDetailDto"
                }
              }
            }
          },
          "401": {
            "description": "Chave ausente, inválida ou revogada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "403": {
            "description": "Falta o escopo models:write, ou o modelo é padrão (somente leitura)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "404": {
            "description": "Não existe nesta conta",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "409": {
            "description": "Já existe um modelo com esse nome (code CONFLICT)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "429": {
            "description": "Mais de 60 requisições por minuto",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Editar modelo do tenant (system = somente leitura)",
        "tags": [
          "Document Models"
        ]
      },
      "delete": {
        "description": "Exclui um modelo da conta. Modelo padrão responde 403. **Requires scope:** `models:delete`",
        "operationId": "deleteDocumentModel",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Id do modelo da conta",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Excluído"
          },
          "401": {
            "description": "Chave ausente, inválida ou revogada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "403": {
            "description": "Falta o escopo models:delete, ou o modelo é padrão (somente leitura)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "404": {
            "description": "Não existe nesta conta",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "429": {
            "description": "Mais de 60 requisições por minuto",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Excluir modelo do tenant (system bloqueado)",
        "tags": [
          "Document Models"
        ]
      }
    },
    "/api/v1/wallet": {
      "get": {
        "description": "Saldo da carteira de créditos da conta, em reais. **Requires scope:** `wallet:read`",
        "operationId": "getWallet",
        "parameters": [],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WalletDto"
                }
              }
            }
          },
          "401": {
            "description": "Chave ausente, inválida ou revogada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "403": {
            "description": "Falta o escopo wallet:read",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "429": {
            "description": "Mais de 60 requisições por minuto",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Consultar saldo e créditos disponíveis",
        "tags": [
          "Wallet"
        ]
      }
    },
    "/api/v1/wallet/consumption": {
      "get": {
        "description": "Soma das transações de consumo no período (sem datas = desde o início). **Requires scope:** `wallet:read`",
        "operationId": "getConsumption",
        "parameters": [
          {
            "name": "createdBefore",
            "required": false,
            "in": "query",
            "description": "Fim do período (ISO 8601)",
            "schema": {}
          },
          {
            "name": "createdAfter",
            "required": false,
            "in": "query",
            "description": "Início do período (ISO 8601)",
            "schema": {
              "example": "2026-10-01T00:00:00Z"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WalletConsumptionDto"
                }
              }
            }
          },
          "401": {
            "description": "Chave ausente, inválida ou revogada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "403": {
            "description": "Falta o escopo wallet:read",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "429": {
            "description": "Mais de 60 requisições por minuto",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Consultar consumo agregado",
        "tags": [
          "Wallet"
        ]
      }
    },
    "/api/v1/transactions": {
      "get": {
        "description": "Extrato da carteira, do mais recente para o mais antigo. **Requires scope:** `transactions:read`",
        "operationId": "listTransactions",
        "parameters": [
          {
            "name": "createdBefore",
            "required": false,
            "in": "query",
            "description": "Até (ISO 8601)",
            "schema": {}
          },
          {
            "name": "createdAfter",
            "required": false,
            "in": "query",
            "description": "A partir de (ISO 8601)",
            "schema": {}
          },
          {
            "name": "type",
            "required": false,
            "in": "query",
            "description": "Tipo da transação",
            "schema": {
              "enum": [
                "RECHARGE",
                "CONSUMPTION",
                "REFUND",
                "BONUS",
                "COUPON",
                "ADJUSTMENT",
                "EXPIRATION",
                "ADDON_CHARGE",
                "ASSISTANT_ACTIVATION",
                "ASSISTANT_RENEWAL",
                "HOLD",
                "RELEASE",
                "SETTLEMENT",
                "RECHARGE_REFUND"
              ],
              "type": "string"
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Itens por página (máx. 100)",
            "schema": {
              "example": 20,
              "type": "number"
            }
          },
          {
            "name": "page",
            "required": false,
            "in": "query",
            "description": "Página (começa em 1)",
            "schema": {
              "example": 1,
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WalletTransactionListPageDto"
                }
              }
            }
          },
          "400": {
            "description": "Valor de filtro inválido",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationErrorResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "Chave ausente, inválida ou revogada",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "403": {
            "description": "Falta o escopo transactions:read",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "429": {
            "description": "Mais de 60 requisições por minuto",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Histórico financeiro paginado",
        "tags": [
          "Transactions"
        ]
      }
    },
    "/api/v1/workflows": {
      "get": {
        "description": "**Requires scope:** `workflows:read`",
        "operationId": "listWorkflows",
        "parameters": [
          {
            "name": "page",
            "required": false,
            "in": "query",
            "description": "Página.",
            "schema": {
              "minimum": 1,
              "default": 1,
              "type": "number"
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Itens por página.",
            "schema": {
              "minimum": 1,
              "maximum": 100,
              "default": 20,
              "type": "number"
            }
          },
          {
            "name": "search",
            "required": false,
            "in": "query",
            "description": "Busca no nome e na descrição.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "active",
            "required": false,
            "in": "query",
            "description": "`true` só ativos; `false` só inativos.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorkflowListResponseDto"
                }
              }
            }
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Listar workflows",
        "tags": [
          "Workflows"
        ]
      },
      "post": {
        "description": "Cria o rascunho. Com `publish: true`, publica a versão 1 e ativa (validação completa: modelos e campos precisam existir na conta; se falhar, nada é criado). **Requires scope:** `workflows:write`",
        "operationId": "createWorkflow",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateWorkflowDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorkflowDto"
                }
              }
            }
          },
          "400": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationErrorResponseDto"
                }
              }
            }
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Criar workflow",
        "tags": [
          "Workflows"
        ]
      }
    },
    "/api/v1/workflows/{id}": {
      "get": {
        "description": "Rascunho (`definition`) e versão publicada (`publishedDefinition`) no formato público: perguntas, documentos pedidos e etapas. **Requires scope:** `workflows:read`",
        "operationId": "getWorkflow",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "ID do workflow (UUID)",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorkflowDto"
                }
              }
            }
          },
          "404": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Consultar workflow",
        "tags": [
          "Workflows"
        ]
      },
      "patch": {
        "description": "Edita o rascunho. `questions`, `documents` e `steps`, quando enviados, substituem a seção inteira. Processos em andamento seguem na versão em que começaram; `publish: true` publica a nova versão. **Requires scope:** `workflows:write`",
        "operationId": "updateWorkflow",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "ID do workflow (UUID)",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateWorkflowDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorkflowDto"
                }
              }
            }
          },
          "400": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationErrorResponseDto"
                }
              }
            }
          },
          "404": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Editar workflow",
        "tags": [
          "Workflows"
        ]
      },
      "delete": {
        "description": "Bloqueado com processo em andamento ou envio recorrente ativo/pausado. **Requires scope:** `workflows:delete`",
        "operationId": "deleteWorkflow",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "ID do workflow (UUID)",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": ""
          },
          "400": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Excluir workflow",
        "tags": [
          "Workflows"
        ]
      }
    },
    "/api/v1/workflows/{id}/publish": {
      "post": {
        "description": "Publica o rascunho como nova versão e ativa o workflow. **Requires scope:** `workflows:write`",
        "operationId": "publish",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "ID do workflow (UUID)",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorkflowDto"
                }
              }
            }
          },
          "400": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Publicar workflow",
        "tags": [
          "Workflows"
        ]
      }
    },
    "/api/v1/workflows/{id}/invitations": {
      "post": {
        "description": "Abre um processo para uma pessoa e devolve o link pessoal. Com `sendEmail: false`, só gera o link. Cada arquivo que a pessoa enviar é cobrado da carteira como um documento. **Requires scope:** `workflows:invite`",
        "operationId": "invite",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "ID do workflow (UUID), publicado e ativo",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateWorkflowInvitationDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorkflowInvitationCreatedDto"
                }
              }
            }
          },
          "400": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationErrorResponseDto"
                }
              }
            }
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Enviar convite",
        "tags": [
          "Workflows"
        ]
      }
    },
    "/api/v1/workflow-processes": {
      "get": {
        "description": "Filtre por `email`, `cpf` ou `workflowId` (e `situation`, datas). Cada processo traz todos os documentos pedidos: os enviados com `file` (link de download temporário) e `data` (dados lidos, mascarados); os ainda não enviados com `file` e `data` nulos. `?reveal=true` exige `documents:read:sensitive`. **Requires scope:** `workflows:read`",
        "operationId": "listWorkflowProcesses",
        "parameters": [
          {
            "name": "page",
            "required": false,
            "in": "query",
            "description": "Página.",
            "schema": {
              "minimum": 1,
              "default": 1,
              "type": "number"
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Processos por página (máx. 50).",
            "schema": {
              "minimum": 1,
              "maximum": 50,
              "default": 20,
              "type": "number"
            }
          },
          {
            "name": "workflowId",
            "required": false,
            "in": "query",
            "description": "Só processos deste workflow (UUID).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "email",
            "required": false,
            "in": "query",
            "description": "E-mail da pessoa convidada (exato, sem diferenciar maiúsculas).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "cpf",
            "required": false,
            "in": "query",
            "description": "CPF da pessoa, com ou sem máscara (11 dígitos). Confere o CPF informado no convite e o do cadastro do convidado.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "situation",
            "required": false,
            "in": "query",
            "description": "Só processos nesta situação.",
            "schema": {
              "type": "string",
              "enum": [
                "LINK_CREATED",
                "SENT",
                "IN_PROGRESS",
                "OVERDUE",
                "EXPIRED",
                "PENDING",
                "SUBMITTED_LATE",
                "FINISHED",
                "FINISHED_LATE",
                "REJECTED",
                "CANCELLED"
              ]
            }
          },
          {
            "name": "createdAfter",
            "required": false,
            "in": "query",
            "description": "Criados a partir desta data (ISO 8601).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "createdBefore",
            "required": false,
            "in": "query",
            "description": "Criados até esta data (ISO 8601).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "reveal",
            "required": false,
            "in": "query",
            "description": "Devolve CPF e números sem máscara. Exige o escopo `documents:read:sensitive` e fica registrado no histórico de cada processo.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorkflowProcessListResponseDto"
                }
              }
            }
          },
          "403": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Listar processos",
        "tags": [
          "Workflow Processes"
        ]
      }
    },
    "/api/v1/workflow-processes/{id}": {
      "get": {
        "description": "Mesmo formato da listagem, com a conferência dos documentos atualizada na hora. **Requires scope:** `workflows:read`",
        "operationId": "getWorkflowProcess",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "ID do processo (UUID)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "reveal",
            "required": false,
            "in": "query",
            "description": "Devolve CPF e números sem máscara. Exige o escopo `documents:read:sensitive` e fica registrado no histórico do processo.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorkflowProcessDto"
                }
              }
            }
          },
          "404": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Consultar processo",
        "tags": [
          "Workflow Processes"
        ]
      }
    },
    "/api/v1/assistant/questions": {
      "post": {
        "description": "Responde em linguagem natural usando **só** os documentos do `scope` (por IDs, CPF/CNPJ das partes ou processo de workflow). O escopo é resolvido no NouVy e nunca é o acervo inteiro. Quem pergunta (`externalUserId`) é autenticado pelo seu sistema. Cada resposta encontrada é cobrada da carteira; \"não encontrei\" não cobra. **Requires scope:** `assistant:ask`",
        "operationId": "ask",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AskAssistantQuestionDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AssistantAnswerDto"
                }
              }
            }
          },
          "400": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationErrorResponseDto"
                }
              }
            }
          },
          "402": {
            "description": "Saldo insuficiente na carteira",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "404": {
            "description": "`ASSISTANT_SCOPE_EMPTY`: nenhum documento no escopo; ou conversa de outro usuário/escopo",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "422": {
            "description": "`ASSISTANT_SCOPE_REQUIRED` ou `ASSISTANT_SCOPE_TOO_BROAD` (mais de 200 documentos)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "429": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Perguntar sobre documentos escolhidos",
        "tags": [
          "Assistant"
        ]
      }
    },
    "/api/v1/assistant/sessions": {
      "post": {
        "description": "Chamado pelo **seu servidor**, depois de autenticar o usuário. Devolve um token curto (`nvy_ses_…`) preso ao `externalUserId` e ao `scope`, para o widget conversar direto do navegador sem expor a chave. O navegador não consegue mudar nem ampliar o escopo. Revogar a chave encerra as sessões abertas por ela. **Requires scope:** `assistant:ask`",
        "operationId": "createSession",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateAssistantSessionDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AssistantSessionDto"
                }
              }
            }
          },
          "400": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationErrorResponseDto"
                }
              }
            }
          },
          "404": {
            "description": "`ASSISTANT_SCOPE_EMPTY`: nenhum documento no escopo",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "422": {
            "description": "`ASSISTANT_SCOPE_REQUIRED` ou `ASSISTANT_SCOPE_TOO_BROAD` (mais de 200 documentos)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "429": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Abrir sessão do assistente para o navegador",
        "tags": [
          "Assistant"
        ]
      }
    },
    "/api/v1/assistant/session/messages": {
      "post": {
        "description": "Usado pelo widget, do navegador do usuário, com `Authorization: Bearer nvy_ses_…`. Responde só a partir do escopo gravado na sessão. A cobrança é a mesma de `POST /assistant/questions`, sem o valor na resposta.",
        "operationId": "ask",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AssistantSessionQuestionDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AssistantSessionAnswerDto"
                }
              }
            }
          },
          "400": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationErrorResponseDto"
                }
              }
            }
          },
          "401": {
            "description": "`ASSISTANT_SESSION_EXPIRED`: sessão vencida, inexistente ou chave revogada. Abra outra pelo servidor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "402": {
            "description": "Saldo insuficiente na carteira",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "404": {
            "description": "`ASSISTANT_SCOPE_EMPTY` (os documentos saíram do escopo) ou conversa de outra sessão",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          },
          "429": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseDto"
                }
              }
            }
          }
        },
        "security": [
          {
            "assistant-session": []
          }
        ],
        "summary": "Perguntar com a sessão (navegador)",
        "tags": [
          "Assistant"
        ]
      }
    }
  },
  "info": {
    "title": "Nouvy Public API",
    "description": "API REST pública do Nouvy para integrações de clientes (OpenAPI 3.1).\n\nGuias: [/docs](/docs) · Spec: [/openapi.json](/openapi.json)\n\n## Autenticação\n\n`Authorization: Bearer <keyPrefix>.<secret>`\n\n- Prefixo novo: `nvy_<12hex>`\n- Prefixo legado aceito: `nvk_live_*`\n- Todas as chaves são de **produção** (não há sandbox/dev)\n\n## Gestão de API Keys\n\nCriação e listagem administrativas ocorrem na interface web da plataforma.\nVia REST: `GET /api/v1/auth/me` e `POST /api/v1/auth/revoke`.\n\n## Rate Limiting\n\n60 requisições/minuto por API Key.\nHeaders: `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, `Retry-After`.\n\n## Erros\n\nRFC 7807 Problem Details (`type`, `title`, `status`, `detail`, `instance`, `code`, `requestId`, `timestamp`).\n\n## Paginação\n\n`{ items: [], pagination: { page, limit, totalItems, totalPages, hasNext, hasPrevious } }`\n",
    "version": "1.0.0",
    "contact": {}
  },
  "tags": [
    {
      "name": "Health",
      "description": "Verificação de saúde"
    },
    {
      "name": "Authentication",
      "description": "Chave atual e revogação"
    },
    {
      "name": "Documents",
      "description": "Upload e documentos"
    },
    {
      "name": "Entities",
      "description": "Entidades extraídas"
    },
    {
      "name": "Products",
      "description": "Produtos detectados"
    },
    {
      "name": "Document Models",
      "description": "Modelos de documento"
    },
    {
      "name": "Workflows",
      "description": "Workflows: documentos pedidos, regras, etapas e convites"
    },
    {
      "name": "Workflow Processes",
      "description": "Processos dos workflows: situação, arquivos enviados e dados lidos"
    },
    {
      "name": "Assistant",
      "description": "Perguntas sobre documentos de um escopo"
    },
    {
      "name": "Wallet",
      "description": "Carteira e créditos"
    },
    {
      "name": "Transactions",
      "description": "Histórico financeiro"
    }
  ],
  "servers": [],
  "components": {
    "securitySchemes": {
      "api-key": {
        "scheme": "bearer",
        "bearerFormat": "API_KEY",
        "type": "http",
        "description": "API Key no formato: <keyPrefix>.<secret> (ex: nvy_abc123def456.xyz789secret)"
      },
      "assistant-session": {
        "scheme": "bearer",
        "bearerFormat": "SESSION_TOKEN",
        "type": "http",
        "description": "Token da sessão do assistente (nvy_ses_…), aberto pelo seu servidor em POST /api/v1/assistant/sessions."
      }
    },
    "schemas": {
      "HealthDto": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "example": "ok"
          },
          "timestamp": {
            "type": "string",
            "example": "2026-10-09T18:00:00.000Z"
          }
        },
        "required": [
          "status",
          "timestamp"
        ]
      },
      "DocumentFileUrlDto": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "description": "URL assinada para baixar o arquivo original"
          },
          "expiresInSeconds": {
            "type": "number",
            "description": "Validade da URL em segundos",
            "example": 600
          },
          "fileName": {
            "type": "string"
          },
          "mimeType": {
            "type": "string",
            "example": "application/pdf"
          }
        },
        "required": [
          "url",
          "expiresInSeconds",
          "fileName",
          "mimeType"
        ]
      },
      "ErrorResponseDto": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "description": "URI do problema (RFC 7807), derivada do `code`",
            "example": "https://api.nouvy.cloud/errors/unauthenticated"
          },
          "title": {
            "type": "string",
            "description": "Nome do status HTTP",
            "example": "UNAUTHORIZED"
          },
          "status": {
            "type": "number",
            "description": "Código HTTP de status",
            "example": 401
          },
          "detail": {
            "type": "string",
            "description": "Mensagem legível para pessoas (não use para decidir o fluxo; use `code`)",
            "example": "Invalid or expired API key"
          },
          "instance": {
            "type": "string",
            "description": "Caminho da requisição que causou o erro",
            "example": "/api/v1/documents"
          },
          "code": {
            "type": "string",
            "description": "Código estável para tratar no seu sistema: UNAUTHENTICATED (401), UNAUTHORIZED (403), NOT_FOUND (404), VALIDATION_ERROR (400), UPLOAD_* (400/413), RATE_LIMIT_EXCEEDED (429), WORKFLOW_RULE (400), INTERNAL_ERROR (500)",
            "example": "UNAUTHENTICATED"
          },
          "requestId": {
            "type": "string",
            "description": "ID único da requisição para rastreamento",
            "example": "req_1234567890abcdef"
          },
          "timestamp": {
            "type": "string",
            "description": "Timestamp do erro",
            "example": "2024-01-15T10:30:00.000Z"
          },
          "retryable": {
            "type": "boolean",
            "description": "true quando repetir a mesma requisição mais tarde pode dar certo (429, 503, 500)",
            "example": false
          },
          "details": {
            "type": "object",
            "description": "Dados extras do erro, quando houver (ex.: `maxBytes` em UPLOAD_TOO_LARGE)",
            "example": {
              "maxBytes": 20971520
            }
          }
        },
        "required": [
          "type",
          "title",
          "status",
          "detail",
          "code",
          "requestId",
          "timestamp",
          "retryable"
        ]
      },
      "DocumentExtractionDto": {
        "type": "object",
        "properties": {
          "classification": {
            "type": "object",
            "nullable": true,
            "description": "Modelo em que o documento foi classificado"
          },
          "confidence": {
            "type": "object",
            "nullable": true,
            "description": "Confiança média da extração (0 a 1)"
          },
          "classificationConfidence": {
            "type": "object",
            "nullable": true,
            "description": "Confiança da classificação (0 a 1)"
          },
          "classificationRoute": {
            "type": "string",
            "nullable": true,
            "enum": [
              "AUTO",
              "VERIFY",
              "UNKNOWN"
            ],
            "description": "AUTO = classificado com segurança; VERIFY = vale conferir; UNKNOWN = sem modelo"
          },
          "processingStatus": {
            "type": "object",
            "nullable": true,
            "example": "COMPLETED"
          },
          "fields": {
            "type": "object",
            "additionalProperties": true,
            "nullable": true,
            "description": "Campos do modelo, achatados (chave do campo → valor lido). Para o JSON estruturado use GET /documents/{id}/data"
          },
          "raw": {
            "type": "object",
            "additionalProperties": true,
            "nullable": true,
            "description": "Saída técnica da extração (confiança por campo, evidências)"
          }
        }
      },
      "DocumentProductLineDto": {
        "type": "object",
        "properties": {
          "productId": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "ncm": {
            "type": "object",
            "nullable": true
          },
          "unit": {
            "type": "object",
            "nullable": true
          },
          "direction": {
            "type": "string",
            "nullable": true,
            "enum": [
              "IN",
              "OUT",
              "UNKNOWN"
            ],
            "description": "Entrada ou saída para a conta"
          },
          "quantity": {
            "type": "object",
            "nullable": true
          },
          "unitValue": {
            "type": "object",
            "nullable": true,
            "description": "Em reais"
          },
          "totalValue": {
            "type": "object",
            "nullable": true,
            "description": "Em reais"
          },
          "icms": {
            "type": "object",
            "nullable": true,
            "description": "Em reais"
          },
          "ipi": {
            "type": "object",
            "nullable": true,
            "description": "Em reais"
          },
          "aliquotaIcms": {
            "type": "object",
            "nullable": true,
            "description": "Percentual"
          },
          "aliquotaIpi": {
            "type": "object",
            "nullable": true,
            "description": "Percentual"
          },
          "cfop": {
            "type": "object",
            "nullable": true
          },
          "cst": {
            "type": "object",
            "nullable": true
          },
          "lineIndex": {
            "type": "object",
            "nullable": true,
            "description": "Posição da linha na nota"
          },
          "lote": {
            "type": "object",
            "nullable": true
          },
          "ean": {
            "type": "object",
            "nullable": true
          }
        },
        "required": [
          "productId",
          "description"
        ]
      },
      "DocumentDetailDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "fileName": {
            "type": "string"
          },
          "mimeType": {
            "type": "string"
          },
          "fileSize": {
            "type": "number",
            "description": "Tamanho em bytes"
          },
          "status": {
            "type": "string",
            "enum": [
              "CREATED",
              "UPLOADING",
              "UPLOADED",
              "PROCESSING",
              "AWAITING_APPROVAL",
              "COMPLETED",
              "FAILED"
            ]
          },
          "errorMessage": {
            "type": "object",
            "nullable": true,
            "description": "Motivo, quando status = FAILED"
          },
          "createdAt": {
            "type": "string"
          },
          "updatedAt": {
            "type": "string"
          },
          "extraction": {
            "nullable": true,
            "description": "Nulo até a extração terminar",
            "allOf": [
              {
                "$ref": "#/components/schemas/DocumentExtractionDto"
              }
            ]
          },
          "products": {
            "description": "Linhas de produto (notas fiscais de produto)",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DocumentProductLineDto"
            }
          }
        },
        "required": [
          "id",
          "fileName",
          "mimeType",
          "fileSize",
          "status",
          "createdAt",
          "updatedAt",
          "products"
        ]
      },
      "DocumentDataModelDto": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string",
            "example": "nfse"
          },
          "name": {
            "type": "string",
            "example": "NFS-e"
          },
          "origin": {
            "type": "string",
            "enum": [
              "SYSTEM",
              "TENANT"
            ]
          },
          "version": {
            "type": "object",
            "example": 3
          },
          "verified": {
            "type": "boolean",
            "description": "Conferido contra documento real."
          }
        },
        "required": [
          "key",
          "name",
          "origin",
          "verified"
        ]
      },
      "DocumentDataDto": {
        "type": "object",
        "properties": {
          "documentId": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "example": "COMPLETED"
          },
          "model": {
            "description": "O modelo classificado. Nulo quando o documento não tem modelo ou extração.",
            "allOf": [
              {
                "$ref": "#/components/schemas/DocumentDataModelDto"
              }
            ]
          },
          "coverage": {
            "type": "string",
            "enum": [
              "COVERED",
              "PARTIAL",
              "NOT_VERIFIED",
              "NOT_COVERED"
            ],
            "description": "COVERED: modelo conferido e documento completo. PARTIAL: obrigatórios faltando neste documento. NOT_VERIFIED: o modelo ainda não foi conferido contra documento real — o JSON vem mesmo assim. NOT_COVERED: sem modelo classificado."
          },
          "completeness": {
            "type": "number",
            "description": "Fração dos campos do modelo que trouxeram valor.",
            "example": 0.48
          },
          "missingRequired": {
            "description": "Caminhos obrigatórios não encontrados.",
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "data": {
            "type": "object",
            "description": "Objeto de negócio. Dinheiro é inteiro em CENTAVOS com moeda — nunca ponto flutuante.",
            "example": {
              "_modelo": "nfse",
              "numero": "123",
              "data_emissao": "2026-01-02",
              "prestador": {
                "cnpj": "41985389000159",
                "nome": "Empresa Prestadora LTDA",
                "email": "contato@empresa.com.br"
              },
              "tomador": {
                "cnpj": "12423787000183",
                "nome": "Empresa Tomadora LTDA"
              },
              "valor_total": {
                "valor": 1400000,
                "moeda": "BRL"
              },
              "iss": null,
              "_estados": {
                "iss": "REDACTED"
              }
            }
          },
          "states": {
            "type": "object",
            "description": "Caminho → estado (NOT_INFORMED, REDACTED, ILLEGIBLE, NOT_APPLICABLE, NOT_FOUND, AMBIGUOUS), para o que o documento não entrega como valor. Um imposto oculto aparece aqui como REDACTED, nunca como R$ 0,00.",
            "example": {
              "tributos.iss.valor": "REDACTED"
            }
          },
          "review": {
            "type": "object",
            "description": "Caminhos que pedem conferência humana, com os motivos. Caminho ausente daqui = sem ressalva.",
            "example": {
              "tomador.endereco": {
                "motivos": [
                  "LOW_CONFIDENCE"
                ]
              }
            }
          },
          "maskedPaths": {
            "description": "Caminhos mascarados por sensibilidade.",
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "unprojectedCount": {
            "type": "number",
            "description": "Fatos extraídos que o schema não prevê. Nada é descartado: o número diz quanto do documento ainda não tem lugar no contrato de negócio."
          }
        },
        "required": [
          "documentId",
          "status",
          "coverage",
          "completeness",
          "missingRequired",
          "states",
          "review",
          "maskedPaths",
          "unprojectedCount"
        ]
      },
      "CreateDocumentDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Nome do documento. No envio por JSON (base64) vira o nome do arquivo: precisa terminar na extensão (ex.: nota.pdf)"
          },
          "type": {
            "type": "string",
            "description": "Rótulo do seu sistema, gravado como metadado de origem. A classificação do documento é automática (não depende deste valor)",
            "enum": [
              "INVOICE",
              "CONTRACT",
              "RECEIPT",
              "OTHER"
            ]
          },
          "metadata": {
            "type": "object",
            "description": "Objeto (ou JSON em texto, no multipart). fileBase64 + mimeType: o arquivo no envio por JSON. tags, origin, applicationName, modelId, workflow: gravados como metadados de origem, para rastreio"
          }
        },
        "required": [
          "name",
          "type"
        ]
      },
      "CreateDocumentResponseDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "duplicated": {
            "type": "boolean"
          },
          "status": {
            "type": "string",
            "example": "PENDING"
          },
          "createdAt": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "status",
          "createdAt"
        ]
      },
      "ValidationErrorResponseDto": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "description": "URI do problema (RFC 7807), derivada do `code`",
            "example": "https://api.nouvy.cloud/errors/unauthenticated"
          },
          "title": {
            "type": "string",
            "description": "Nome do status HTTP",
            "example": "UNAUTHORIZED"
          },
          "status": {
            "type": "number",
            "description": "Código HTTP de status",
            "example": 401
          },
          "detail": {
            "type": "string",
            "description": "Mensagem legível para pessoas (não use para decidir o fluxo; use `code`)",
            "example": "Invalid or expired API key"
          },
          "instance": {
            "type": "string",
            "description": "Caminho da requisição que causou o erro",
            "example": "/api/v1/documents"
          },
          "code": {
            "type": "string",
            "description": "Código estável para tratar no seu sistema: UNAUTHENTICATED (401), UNAUTHORIZED (403), NOT_FOUND (404), VALIDATION_ERROR (400), UPLOAD_* (400/413), RATE_LIMIT_EXCEEDED (429), WORKFLOW_RULE (400), INTERNAL_ERROR (500)",
            "example": "UNAUTHENTICATED"
          },
          "requestId": {
            "type": "string",
            "description": "ID único da requisição para rastreamento",
            "example": "req_1234567890abcdef"
          },
          "timestamp": {
            "type": "string",
            "description": "Timestamp do erro",
            "example": "2024-01-15T10:30:00.000Z"
          },
          "retryable": {
            "type": "boolean",
            "description": "true quando repetir a mesma requisição mais tarde pode dar certo (429, 503, 500)",
            "example": false
          },
          "details": {
            "type": "object",
            "description": "Dados extras do erro, quando houver (ex.: `maxBytes` em UPLOAD_TOO_LARGE)",
            "example": {
              "maxBytes": 20971520
            }
          },
          "errors": {
            "type": "object",
            "description": "Mensagens de validação dos campos (chave `_` reúne as mensagens do corpo e da query)",
            "example": {
              "_": [
                "name should not be empty"
              ]
            }
          }
        },
        "required": [
          "type",
          "title",
          "status",
          "detail",
          "code",
          "requestId",
          "timestamp",
          "retryable"
        ]
      },
      "DocumentListItemDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "fileName": {
            "type": "string",
            "example": "nota-001.pdf"
          },
          "mimeType": {
            "type": "string",
            "example": "application/pdf"
          },
          "fileSize": {
            "type": "number",
            "description": "Tamanho em bytes"
          },
          "status": {
            "type": "string",
            "enum": [
              "CREATED",
              "UPLOADING",
              "UPLOADED",
              "PROCESSING",
              "AWAITING_APPROVAL",
              "COMPLETED",
              "FAILED"
            ]
          },
          "type": {
            "type": "object",
            "description": "Nome do modelo em que o documento foi classificado (ex.: \"NFS-e\"); nulo antes da classificação",
            "nullable": true
          },
          "origin": {
            "type": "object",
            "description": "Canal de entrada (API, EMAIL, DESKTOP_AGENT…)",
            "nullable": true
          },
          "createdAt": {
            "type": "string"
          },
          "updatedAt": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "fileName",
          "mimeType",
          "fileSize",
          "status",
          "createdAt",
          "updatedAt"
        ]
      },
      "PaginationMetaDto": {
        "type": "object",
        "properties": {
          "page": {
            "type": "number",
            "example": 1
          },
          "limit": {
            "type": "number",
            "example": 20
          },
          "totalItems": {
            "type": "number",
            "example": 245
          },
          "totalPages": {
            "type": "number",
            "example": 13
          },
          "hasNext": {
            "type": "boolean",
            "example": true
          },
          "hasPrevious": {
            "type": "boolean",
            "example": false
          }
        },
        "required": [
          "page",
          "limit",
          "totalItems",
          "totalPages",
          "hasNext",
          "hasPrevious"
        ]
      },
      "DocumentListPageDto": {
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DocumentListItemDto"
            }
          },
          "pagination": {
            "$ref": "#/components/schemas/PaginationMetaDto"
          }
        },
        "required": [
          "items",
          "pagination"
        ]
      },
      "ApiKeyMeDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Id da chave"
          },
          "name": {
            "type": "string",
            "description": "Nome dado à chave no painel"
          },
          "description": {
            "type": "object",
            "description": "Descrição opcional",
            "nullable": true
          },
          "keyPrefix": {
            "type": "string",
            "description": "Parte pública do token (antes do ponto)",
            "example": "nvy_abc123def456"
          },
          "scopes": {
            "description": "Escopos concedidos",
            "example": [
              "documents:read",
              "documents:write"
            ],
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "status": {
            "type": "string",
            "enum": [
              "ACTIVE",
              "REVOKED"
            ]
          },
          "createdByUserId": {
            "type": "object",
            "description": "Usuário que criou a chave",
            "nullable": true
          },
          "lastUsedAt": {
            "type": "object",
            "description": "Último uso registrado",
            "nullable": true
          },
          "expiresAt": {
            "type": "object",
            "description": "Expiração; nulo = não expira",
            "nullable": true
          },
          "createdAt": {
            "type": "string"
          },
          "apiClientId": {
            "type": "string",
            "description": "Integração (cliente de API) dona da chave"
          },
          "apiClientName": {
            "type": "string",
            "description": "Nome da integração"
          }
        },
        "required": [
          "id",
          "name",
          "keyPrefix",
          "scopes",
          "status",
          "createdAt",
          "apiClientId",
          "apiClientName"
        ]
      },
      "EntityListItemDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string",
            "example": "Empresa Exemplo Ltda"
          },
          "entityType": {
            "type": "string",
            "enum": [
              "pf",
              "pj",
              "orgao"
            ],
            "description": "pf = pessoa física, pj = pessoa jurídica, orgao = órgão público"
          },
          "taxId": {
            "type": "object",
            "description": "CPF mascarado (***.***.***-09) ou CNPJ inteiro",
            "nullable": true
          },
          "documentsCount": {
            "type": "number",
            "description": "Documentos em que a entidade aparece"
          },
          "confidenceScore": {
            "type": "object",
            "description": "Confiança da identificação (0 a 1)",
            "nullable": true
          },
          "createdAt": {
            "type": "object",
            "nullable": true
          },
          "updatedAt": {
            "type": "object",
            "nullable": true
          }
        },
        "required": [
          "id",
          "name",
          "entityType",
          "documentsCount"
        ]
      },
      "EntityListPageDto": {
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EntityListItemDto"
            }
          },
          "pagination": {
            "$ref": "#/components/schemas/PaginationMetaDto"
          }
        },
        "required": [
          "items",
          "pagination"
        ]
      },
      "EntityDocumentRefDto": {
        "type": "object",
        "properties": {
          "documentId": {
            "type": "string"
          },
          "fileName": {
            "type": "string"
          },
          "modelKey": {
            "type": "object",
            "description": "Modelo do documento (ex.: nfse)",
            "nullable": true
          },
          "status": {
            "type": "string",
            "example": "COMPLETED"
          },
          "role": {
            "type": "object",
            "description": "Papel(is) da entidade nesse documento, separados por vírgula",
            "example": "TOMADOR"
          }
        },
        "required": [
          "documentId",
          "fileName",
          "status"
        ]
      },
      "EntityDetailDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string",
            "example": "Empresa Exemplo Ltda"
          },
          "entityType": {
            "type": "string",
            "enum": [
              "pf",
              "pj",
              "orgao"
            ],
            "description": "pf = pessoa física, pj = pessoa jurídica, orgao = órgão público"
          },
          "taxId": {
            "type": "object",
            "description": "CPF mascarado (***.***.***-09) ou CNPJ inteiro",
            "nullable": true
          },
          "documentsCount": {
            "type": "number",
            "description": "Documentos em que a entidade aparece"
          },
          "confidenceScore": {
            "type": "object",
            "description": "Confiança da identificação (0 a 1)",
            "nullable": true
          },
          "createdAt": {
            "type": "object",
            "nullable": true
          },
          "updatedAt": {
            "type": "object",
            "nullable": true
          },
          "emails": {
            "description": "E-mails encontrados nos documentos",
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "roles": {
            "type": "object",
            "description": "Quantas vezes a entidade apareceu em cada papel",
            "example": {
              "PRESTADOR": 4,
              "TOMADOR": 1
            },
            "additionalProperties": {
              "type": "number"
            }
          },
          "documents": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EntityDocumentRefDto"
            }
          }
        },
        "required": [
          "id",
          "name",
          "entityType",
          "documentsCount",
          "emails",
          "roles",
          "documents"
        ]
      },
      "ProductStockDto": {
        "type": "object",
        "properties": {
          "balance": {
            "type": "number",
            "description": "Saldo em estoque (entradas − saídas)"
          },
          "qtyIn": {
            "type": "number",
            "description": "Quantidade que entrou"
          },
          "qtyOut": {
            "type": "number",
            "description": "Quantidade que saiu"
          },
          "movementsCount": {
            "type": "number"
          },
          "lastMovementAt": {
            "type": "object",
            "description": "Data (AAAA-MM-DD) da última movimentação",
            "nullable": true
          },
          "avgUnitCost": {
            "type": "number",
            "description": "Custo médio das entradas, em reais"
          },
          "lastUnitValue": {
            "type": "number",
            "description": "Último valor unitário, em reais"
          },
          "minUnitValue": {
            "type": "object",
            "nullable": true,
            "description": "Menor valor unitário, em reais"
          },
          "maxUnitValue": {
            "type": "object",
            "nullable": true,
            "description": "Maior valor unitário, em reais"
          },
          "totalInValue": {
            "type": "number",
            "description": "Valor total das entradas, em reais"
          },
          "totalOutValue": {
            "type": "number",
            "description": "Valor total das saídas, em reais"
          },
          "totalIcms": {
            "type": "number",
            "description": "ICMS somado, em reais"
          },
          "totalIpi": {
            "type": "number",
            "description": "IPI somado, em reais"
          },
          "detectedQuantity": {
            "type": "number",
            "description": "Quantidade lida nas notas, inclusive linhas sem entrada/saída definida"
          },
          "detectedValue": {
            "type": "number",
            "description": "Valor lido nas notas, em reais"
          },
          "detectedIcms": {
            "type": "number",
            "description": "ICMS lido nas notas, em reais"
          },
          "stockPending": {
            "type": "boolean",
            "description": "true quando há notas mas nenhuma definiu entrada ou saída"
          },
          "movementValue": {
            "type": "number",
            "description": "Valor movimentado (entradas + saídas), em reais"
          },
          "stockStatus": {
            "type": "string",
            "enum": [
              "OK",
              "ESTOQUE_BAIXO",
              "SEM_ESTOQUE",
              "SALDO_NEGATIVO",
              "SEM_MINIMO_CONFIGURADO"
            ]
          }
        },
        "required": [
          "balance",
          "qtyIn",
          "qtyOut",
          "movementsCount",
          "avgUnitCost",
          "lastUnitValue",
          "totalInValue",
          "totalOutValue",
          "totalIcms",
          "totalIpi",
          "detectedQuantity",
          "detectedValue",
          "detectedIcms",
          "stockPending",
          "movementValue",
          "stockStatus"
        ]
      },
      "ProductListItemDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "description": {
            "type": "string",
            "description": "Descrição principal do produto"
          },
          "nameVariations": {
            "description": "Outras descrições com que o produto apareceu nas notas",
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "ncm": {
            "type": "object",
            "nullable": true,
            "example": "61091000"
          },
          "unit": {
            "type": "object",
            "nullable": true,
            "example": "UN"
          },
          "category": {
            "type": "object",
            "nullable": true
          },
          "sku": {
            "type": "object",
            "nullable": true,
            "description": "Código do produto"
          },
          "marca": {
            "type": "object",
            "nullable": true
          },
          "modelo": {
            "type": "object",
            "nullable": true
          },
          "grupo": {
            "type": "object",
            "nullable": true
          },
          "color": {
            "type": "object",
            "nullable": true
          },
          "size": {
            "type": "object",
            "nullable": true
          },
          "status": {
            "type": "string",
            "example": "active"
          },
          "documentsCount": {
            "type": "number",
            "description": "Notas em que o produto aparece"
          },
          "minQuantity": {
            "type": "object",
            "nullable": true,
            "description": "Estoque mínimo configurado"
          },
          "maxQuantity": {
            "type": "object",
            "nullable": true,
            "description": "Estoque máximo configurado"
          },
          "openDivergences": {
            "type": "number",
            "description": "Divergências em aberto"
          },
          "uiStatus": {
            "type": "string",
            "enum": [
              "ok",
              "atencao",
              "divergencia"
            ],
            "description": "Resumo para exibição"
          },
          "companiesCount": {
            "type": "number",
            "description": "Empresas da conta com movimentação do produto"
          },
          "stockByCompany": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Saldo por empresa da conta"
          },
          "stock": {
            "$ref": "#/components/schemas/ProductStockDto"
          }
        },
        "required": [
          "id",
          "description",
          "nameVariations",
          "status",
          "documentsCount",
          "openDivergences",
          "uiStatus",
          "companiesCount",
          "stockByCompany",
          "stock"
        ]
      },
      "ProductListPageDto": {
        "type": "object",
        "properties": {
          "items": {
            "description": "Com `documentId`, cada item é uma linha da nota (productId, codigo, description, quantity, unitValue…)",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ProductListItemDto"
            }
          },
          "pagination": {
            "$ref": "#/components/schemas/PaginationMetaDto"
          }
        },
        "required": [
          "items",
          "pagination"
        ]
      },
      "ProductDetailDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "description": {
            "type": "string",
            "description": "Descrição principal do produto"
          },
          "nameVariations": {
            "description": "Outras descrições com que o produto apareceu nas notas",
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "ncm": {
            "type": "object",
            "nullable": true,
            "example": "61091000"
          },
          "unit": {
            "type": "object",
            "nullable": true,
            "example": "UN"
          },
          "category": {
            "type": "object",
            "nullable": true
          },
          "sku": {
            "type": "object",
            "nullable": true,
            "description": "Código do produto"
          },
          "marca": {
            "type": "object",
            "nullable": true
          },
          "modelo": {
            "type": "object",
            "nullable": true
          },
          "grupo": {
            "type": "object",
            "nullable": true
          },
          "color": {
            "type": "object",
            "nullable": true
          },
          "size": {
            "type": "object",
            "nullable": true
          },
          "status": {
            "type": "string",
            "example": "active"
          },
          "documentsCount": {
            "type": "number",
            "description": "Notas em que o produto aparece"
          },
          "minQuantity": {
            "type": "object",
            "nullable": true,
            "description": "Estoque mínimo configurado"
          },
          "maxQuantity": {
            "type": "object",
            "nullable": true,
            "description": "Estoque máximo configurado"
          },
          "openDivergences": {
            "type": "number",
            "description": "Divergências em aberto"
          },
          "uiStatus": {
            "type": "string",
            "enum": [
              "ok",
              "atencao",
              "divergencia"
            ],
            "description": "Resumo para exibição"
          },
          "companiesCount": {
            "type": "number",
            "description": "Empresas da conta com movimentação do produto"
          },
          "stockByCompany": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Saldo por empresa da conta"
          },
          "stock": {
            "$ref": "#/components/schemas/ProductStockDto"
          },
          "supplierCodes": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Códigos do produto em cada fornecedor (entityId, code, name, isOwn)"
          },
          "fiscalIdentity": {
            "type": "object",
            "additionalProperties": true,
            "nullable": true,
            "description": "Empresa da conta usada para decidir entrada/saída"
          },
          "movements": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Movimentações (uma por linha de nota): documentId, documentNumber, documentDate, direction IN/OUT/UNKNOWN, quantity, unitValue, totalValue, icms, ipi, cfop, supplierName, appliedToStock…"
          },
          "documents": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Notas em que o produto aparece"
          }
        },
        "required": [
          "id",
          "description",
          "nameVariations",
          "status",
          "documentsCount",
          "openDivergences",
          "uiStatus",
          "companiesCount",
          "stockByCompany",
          "stock",
          "supplierCodes",
          "movements",
          "documents"
        ]
      },
      "DocumentModelDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Id do modelo (catálogo ou da conta)"
          },
          "key": {
            "type": "string",
            "description": "Chave do modelo padrão (ex.: nfse, cnh); ausente em modelo da conta"
          },
          "name": {
            "type": "string",
            "example": "NFS-e"
          },
          "isSystem": {
            "type": "boolean",
            "description": "true = modelo padrão do NouVy (somente leitura)"
          },
          "origin": {
            "type": "string",
            "enum": [
              "SYSTEM",
              "TENANT"
            ]
          },
          "canEdit": {
            "type": "boolean",
            "description": "A chave pode editar/excluir este modelo"
          },
          "customModelId": {
            "type": "object",
            "description": "Id do modelo da conta (quando origin = TENANT)",
            "nullable": true
          }
        },
        "required": [
          "id",
          "name",
          "isSystem",
          "origin",
          "canEdit"
        ]
      },
      "DocumentModelListPageDto": {
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DocumentModelDto"
            }
          },
          "pagination": {
            "$ref": "#/components/schemas/PaginationMetaDto"
          }
        },
        "required": [
          "items",
          "pagination"
        ]
      },
      "DocumentModelFieldDto": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string",
            "example": "validade",
            "description": "Chave usada em requiredFields e nas regras de workflow"
          },
          "label": {
            "type": "string",
            "example": "Validade"
          },
          "type": {
            "type": "string",
            "example": "DATE",
            "description": "TEXT, NUMBER, DATE, DATETIME, BOOLEAN, CPF, CNPJ, CURRENCY, EMAIL, PHONE, URL, BARCODE, LIST…"
          },
          "required": {
            "type": "boolean"
          }
        },
        "required": [
          "key",
          "label",
          "type",
          "required"
        ]
      },
      "DocumentModelDetailDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Id do modelo (catálogo ou da conta)"
          },
          "key": {
            "type": "string",
            "description": "Chave do modelo padrão (ex.: nfse, cnh); ausente em modelo da conta"
          },
          "name": {
            "type": "string",
            "example": "NFS-e"
          },
          "isSystem": {
            "type": "boolean",
            "description": "true = modelo padrão do NouVy (somente leitura)"
          },
          "origin": {
            "type": "string",
            "enum": [
              "SYSTEM",
              "TENANT"
            ]
          },
          "canEdit": {
            "type": "boolean",
            "description": "A chave pode editar/excluir este modelo"
          },
          "customModelId": {
            "type": "object",
            "description": "Id do modelo da conta (quando origin = TENANT)",
            "nullable": true
          },
          "workflowModelKey": {
            "type": "string",
            "example": "cnh",
            "description": "Valor para `modelKey` em workflows (key ou custom:<id>)"
          },
          "fields": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DocumentModelFieldDto"
            }
          }
        },
        "required": [
          "id",
          "name",
          "isSystem",
          "origin",
          "canEdit",
          "workflowModelKey",
          "fields"
        ]
      },
      "CreateDocumentModelBodyDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "example": "Contrato de locação",
            "description": "Nome do modelo"
          },
          "baseModelCatalogId": {
            "type": "object",
            "nullable": true,
            "description": "Id de um modelo do catálogo para usar como base (copia os campos). Omitido = modelo vazio."
          }
        },
        "required": [
          "name"
        ]
      },
      "CreatedDocumentModelDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "isSystem": {
            "type": "boolean",
            "example": false
          },
          "canEdit": {
            "type": "boolean",
            "example": true
          }
        },
        "required": [
          "id",
          "name",
          "isSystem",
          "canEdit"
        ]
      },
      "UpdateDocumentModelBodyDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "example": "Contrato de locação comercial"
          }
        },
        "required": [
          "name"
        ]
      },
      "WalletDto": {
        "type": "object",
        "properties": {
          "balanceBrl": {
            "type": "number",
            "description": "Saldo total, em reais"
          },
          "availableBrl": {
            "type": "number",
            "description": "Disponível para uso (saldo − reservado)"
          },
          "reservedBrl": {
            "type": "number",
            "description": "Reservado para processamentos em andamento"
          },
          "paidBalanceBrl": {
            "type": "number",
            "description": "Parte do saldo que veio de recargas pagas"
          },
          "bonusBalanceBrl": {
            "type": "number",
            "description": "Parte do saldo que veio de bônus e cupons"
          },
          "currency": {
            "type": "string",
            "example": "BRL"
          },
          "status": {
            "type": "string",
            "example": "ACTIVE"
          },
          "updatedAt": {
            "type": "string"
          }
        },
        "required": [
          "balanceBrl",
          "availableBrl",
          "reservedBrl",
          "paidBalanceBrl",
          "bonusBalanceBrl",
          "currency",
          "status",
          "updatedAt"
        ]
      },
      "WalletConsumptionPeriodDto": {
        "type": "object",
        "properties": {
          "from": {
            "type": "object",
            "nullable": true
          },
          "to": {
            "type": "object",
            "nullable": true
          }
        }
      },
      "WalletConsumptionDto": {
        "type": "object",
        "properties": {
          "totalConsumedBrl": {
            "type": "number",
            "description": "Total consumido no período, em reais"
          },
          "transactionCount": {
            "type": "number",
            "description": "Transações de consumo somadas"
          },
          "period": {
            "$ref": "#/components/schemas/WalletConsumptionPeriodDto"
          }
        },
        "required": [
          "totalConsumedBrl",
          "transactionCount",
          "period"
        ]
      },
      "WalletTransactionDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "enum": [
              "RECHARGE",
              "CONSUMPTION",
              "REFUND",
              "BONUS",
              "COUPON",
              "ADJUSTMENT",
              "EXPIRATION",
              "ADDON_CHARGE",
              "ASSISTANT_ACTIVATION",
              "ASSISTANT_RENEWAL",
              "HOLD",
              "RELEASE",
              "SETTLEMENT",
              "RECHARGE_REFUND"
            ]
          },
          "amountBrl": {
            "type": "number",
            "description": "Valor em reais: positivo entra (RECHARGE, BONUS, REFUND…), negativo sai (CONSUMPTION…), 0 em HOLD/RELEASE/SETTLEMENT (só reservam e liberam)"
          },
          "balanceAfterBrl": {
            "type": "number",
            "description": "Saldo depois da transação, em reais"
          },
          "description": {
            "type": "object",
            "nullable": true
          },
          "referenceType": {
            "type": "object",
            "nullable": true,
            "example": "DOCUMENT_PROCESSING",
            "description": "O que originou a transação"
          },
          "referenceId": {
            "type": "object",
            "nullable": true,
            "description": "Id do item que originou (ex.: o documento)"
          },
          "createdAt": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "type",
          "amountBrl",
          "balanceAfterBrl",
          "createdAt"
        ]
      },
      "WalletTransactionListPageDto": {
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WalletTransactionDto"
            }
          },
          "pagination": {
            "$ref": "#/components/schemas/PaginationMetaDto"
          }
        },
        "required": [
          "items",
          "pagination"
        ]
      },
      "WorkflowSummaryDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Id do workflow (UUID)."
          },
          "name": {
            "type": "string",
            "description": "Nome."
          },
          "description": {
            "type": "object",
            "nullable": true,
            "description": "Descrição."
          },
          "active": {
            "type": "boolean",
            "description": "Aceita convites agora (precisa de versão publicada)."
          },
          "publishedVersion": {
            "type": "number",
            "description": "Última versão publicada; `0` = nunca publicado."
          },
          "hasUnpublishedChanges": {
            "type": "boolean",
            "description": "O rascunho tem mudanças que ainda não foram publicadas."
          },
          "documentsCount": {
            "type": "number",
            "description": "Quantos documentos o rascunho pede."
          },
          "createdAt": {
            "format": "date-time",
            "type": "string",
            "description": "Criação (ISO 8601)."
          },
          "updatedAt": {
            "format": "date-time",
            "type": "string",
            "description": "Última alteração (ISO 8601)."
          }
        },
        "required": [
          "id",
          "name",
          "active",
          "publishedVersion",
          "hasUnpublishedChanges",
          "documentsCount",
          "createdAt",
          "updatedAt"
        ]
      },
      "WorkflowListResponseDto": {
        "type": "object",
        "properties": {
          "items": {
            "description": "Workflows da página.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WorkflowSummaryDto"
            }
          },
          "pagination": {
            "description": "Paginação.",
            "allOf": [
              {
                "$ref": "#/components/schemas/PaginationMetaDto"
              }
            ]
          }
        },
        "required": [
          "items",
          "pagination"
        ]
      },
      "WorkflowQuestionDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Id da pergunta: letras minúsculas, números e `_`. Usado em `condition` e `repeat`.",
            "example": "casado"
          },
          "label": {
            "type": "string",
            "description": "Texto da pergunta mostrado à pessoa.",
            "example": "Você é casado(a) ou vive em união estável?"
          },
          "help": {
            "type": "string",
            "description": "Texto de ajuda abaixo da pergunta.",
            "example": "Informe 0 se não tiver."
          },
          "type": {
            "type": "string",
            "enum": [
              "BOOLEAN",
              "NUMBER",
              "SELECT",
              "TEXT"
            ],
            "description": "`BOOLEAN` (sim/não), `NUMBER` (inteiro), `SELECT` (uma das `options`) ou `TEXT`."
          },
          "options": {
            "description": "Opções da pergunta `SELECT`.",
            "example": [
              "CLT",
              "Estágio"
            ],
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "required": {
            "type": "boolean",
            "default": true,
            "description": "A pessoa precisa responder antes de enviar os documentos."
          },
          "max": {
            "type": "number",
            "description": "Pergunta `NUMBER`: maior valor aceito.",
            "example": 10
          }
        },
        "required": [
          "id",
          "label",
          "type"
        ]
      },
      "WorkflowConditionDto": {
        "type": "object",
        "properties": {
          "questionId": {
            "type": "string",
            "description": "Id da pergunta cuja resposta decide se o documento é pedido.",
            "example": "casado"
          },
          "operator": {
            "type": "string",
            "enum": [
              "EQ",
              "NE",
              "GT",
              "GTE",
              "LT",
              "LTE"
            ],
            "example": "EQ",
            "description": "Comparação: igual, diferente, maior, maior ou igual, menor, menor ou igual."
          },
          "value": {
            "type": "object",
            "description": "Valor comparado com a resposta (true/false, número ou texto).",
            "example": true
          }
        },
        "required": [
          "questionId",
          "operator",
          "value"
        ]
      },
      "WorkflowRepeatDto": {
        "type": "object",
        "properties": {
          "countQuestionId": {
            "type": "string",
            "description": "Pergunta `NUMBER` cuja resposta diz quantas vezes o documento é pedido (um por filho, por exemplo).",
            "example": "filhos"
          },
          "max": {
            "type": "number",
            "description": "Limite de repetições (1 a 50).",
            "example": 10
          }
        },
        "required": [
          "countQuestionId"
        ]
      },
      "WorkflowRuleDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Id da regra. Gerado quando omitido (`<id do documento>_<n>`).",
            "example": "identidade_1"
          },
          "type": {
            "type": "string",
            "enum": [
              "NOT_EXPIRED",
              "DATE_IN_PAST",
              "VALID_FORMAT",
              "REGEX",
              "EQUALS",
              "MIN_CONFIDENCE",
              "BELONGS_TO_RECIPIENT",
              "MENTIONS_RECIPIENT"
            ],
            "example": "NOT_EXPIRED",
            "description": "O que conferir. `NOT_EXPIRED`: data do `field` não vencida. `DATE_IN_PAST`: data do `field` não está no futuro. `VALID_FORMAT`: número do `field` válido para o tipo (CPF, CNH, PIS…). `REGEX`: `field` no padrão de `value`. `EQUALS`: `field` igual a `value`. `MIN_CONFIDENCE`: confiança da leitura ≥ `value` (0 a 1). `BELONGS_TO_RECIPIENT`: nome ou CPF do documento é o da pessoa convidada. `MENTIONS_RECIPIENT`: o nome da pessoa aparece em um dos `fields`."
          },
          "field": {
            "type": "string",
            "description": "Chave do campo do modelo conferido (veja `fields` em GET /document-models/{id}).",
            "example": "validade"
          },
          "fields": {
            "description": "Campos considerados por `BELONGS_TO_RECIPIENT` (opcional; padrão: nome e CPF) e `MENTIONS_RECIPIENT` (obrigatório).",
            "example": [
              "nome_pai",
              "nome_mae"
            ],
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "value": {
            "description": "`NOT_EXPIRED`: tolerância em dias. `REGEX`: padrão. `EQUALS`: valor esperado. `MIN_CONFIDENCE`: limite de 0 a 1.",
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "number"
              }
            ],
            "example": 0
          },
          "severity": {
            "type": "string",
            "enum": [
              "BLOCK",
              "REVIEW"
            ],
            "default": "BLOCK",
            "description": "`BLOCK` pede outro arquivo à pessoa na hora. `REVIEW` aceita o documento e marca para a sua equipe conferir."
          },
          "message": {
            "type": "string",
            "description": "Texto próprio mostrado à pessoa quando a regra falha (não inclua dados do documento).",
            "example": "Envie uma CNH dentro da validade."
          }
        },
        "required": [
          "type"
        ]
      },
      "WorkflowAcceptedModelDto": {
        "type": "object",
        "properties": {
          "modelKey": {
            "type": "string",
            "description": "Modelo aceito: `key` do catálogo ou `custom:<customModelId>` para modelo da sua conta (use `workflowModelKey` de GET /document-models/{id}).",
            "example": "cnh"
          },
          "name": {
            "type": "string",
            "description": "Nome mostrado à pessoa (\"Aceitamos: CNH ou RG\").",
            "example": "CNH"
          },
          "requiredFields": {
            "description": "Campos que precisam ser lidos do documento. Se algum não for lido (foto ruim, documento cortado), a pessoa envia outro arquivo na hora.",
            "example": [
              "nome",
              "validade"
            ],
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "rules": {
            "description": "Conferências deste modelo.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WorkflowRuleDto"
            }
          }
        },
        "required": [
          "modelKey"
        ]
      },
      "WorkflowDocumentDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Id estável do documento pedido: letras minúsculas, números e `_` (até 64). Aparece como `slotId` nos processos.",
            "example": "identidade"
          },
          "label": {
            "type": "string",
            "description": "Nome do documento mostrado à pessoa.",
            "example": "Documento de identidade"
          },
          "description": {
            "type": "object",
            "description": "Orientação para a pessoa.",
            "example": "CNH ou RG, frente e verso"
          },
          "holder": {
            "type": "string",
            "enum": [
              "TITULAR",
              "CONJUGE",
              "FILHO",
              "DEPENDENTE",
              "OUTRO"
            ],
            "default": "TITULAR",
            "description": "De quem é o documento. Agrupa o resumo e orienta as regras de titularidade."
          },
          "required": {
            "type": "boolean",
            "default": true,
            "description": "Sem o documento obrigatório a pessoa não consegue enviar para análise."
          },
          "condition": {
            "nullable": true,
            "description": "O documento só é pedido quando a resposta atende a condição.",
            "allOf": [
              {
                "$ref": "#/components/schemas/WorkflowConditionDto"
              }
            ]
          },
          "repeat": {
            "nullable": true,
            "description": "Pede o documento uma vez por unidade da resposta numérica.",
            "allOf": [
              {
                "$ref": "#/components/schemas/WorkflowRepeatDto"
              }
            ]
          },
          "acceptedMimeTypes": {
            "description": "Tipos de arquivo aceitos. Vazio aceita PDF e imagens.",
            "example": [
              "application/pdf",
              "image/jpeg",
              "image/png"
            ],
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "acceptedModels": {
            "description": "Modelos aceitos (alternativas: \"CNH ou RG\"). Obrigatório ter ao menos um ao publicar.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WorkflowAcceptedModelDto"
            }
          }
        },
        "required": [
          "id",
          "label",
          "acceptedModels"
        ]
      },
      "WorkflowStepDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Id da etapa. Gerado quando omitido (`step_<n>_<tipo>`). Ao editar, mantenha o que o GET devolve.",
            "example": "step_1_approval"
          },
          "type": {
            "type": "string",
            "enum": [
              "APPROVAL",
              "NOTIFICATION",
              "INTEGRATION"
            ],
            "description": "`APPROVAL` (sua equipe aprova ou reprova), `NOTIFICATION` (envia e-mail) ou `INTEGRATION` (chama o seu sistema por HTTP)."
          },
          "name": {
            "type": "string",
            "description": "Nome da etapa (aparece no painel e no andamento do processo).",
            "example": "Revisão do RH"
          },
          "config": {
            "type": "object",
            "additionalProperties": true,
            "description": "Configuração da etapa (todos os campos são opcionais). **APPROVAL:** `requireRejectionReason` (boolean, exige motivo ao reprovar), `slaHours` (número, prazo da decisão em horas), `notifyEmails` (lista de e-mails avisados quando o processo chega à aprovação), `notifyInviteeOnReject` (boolean, avisa a pessoa na reprovação). **NOTIFICATION:** `subjectTemplate` e `bodyTemplate` (texto com variáveis `{{convidado.nome}}`, `{{workflow.nome}}`, `{{processo.link}}`…), `recipientEmails` (lista de e-mails), `sendToInvitee` (boolean, envia à pessoa convidada), `sendToInvitationCreator` (boolean; só para convites do painel). **INTEGRATION:** `url` (obrigatória ao publicar; http ou https, endereço público), `method` (`POST` padrão, `PUT`, `PATCH`, `GET`), `headers` (objeto chave → valor), `bodyTemplate` (JSON com variáveis; sem ele o processo inteiro vai em JSON), `timeoutMs` (1000 a 15000, padrão 10000).",
            "example": {
              "requireRejectionReason": true
            }
          }
        },
        "required": [
          "type"
        ]
      },
      "WorkflowDefinitionBodyDto": {
        "type": "object",
        "properties": {
          "questions": {
            "description": "Perguntas.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WorkflowQuestionDto"
            }
          },
          "documents": {
            "description": "Documentos pedidos.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WorkflowDocumentDto"
            }
          },
          "steps": {
            "description": "Etapas depois da coleta, na ordem.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WorkflowStepDto"
            }
          }
        },
        "required": [
          "questions",
          "documents",
          "steps"
        ]
      },
      "WorkflowDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Id do workflow (UUID)."
          },
          "name": {
            "type": "string",
            "description": "Nome."
          },
          "description": {
            "type": "object",
            "nullable": true,
            "description": "Descrição."
          },
          "active": {
            "type": "boolean",
            "description": "Aceita convites agora (precisa de versão publicada)."
          },
          "publishedVersion": {
            "type": "number",
            "description": "Última versão publicada; `0` = nunca publicado."
          },
          "hasUnpublishedChanges": {
            "type": "boolean",
            "description": "O rascunho tem mudanças que ainda não foram publicadas."
          },
          "documentsCount": {
            "type": "number",
            "description": "Quantos documentos o rascunho pede."
          },
          "createdAt": {
            "format": "date-time",
            "type": "string",
            "description": "Criação (ISO 8601)."
          },
          "updatedAt": {
            "format": "date-time",
            "type": "string",
            "description": "Última alteração (ISO 8601)."
          },
          "definition": {
            "description": "Rascunho: o que o PATCH edita e o que a próxima publicação vai usar.",
            "allOf": [
              {
                "$ref": "#/components/schemas/WorkflowDefinitionBodyDto"
              }
            ]
          },
          "publishedDefinition": {
            "nullable": true,
            "description": "Versão publicada, usada pelos convites novos. `null` antes da primeira publicação.",
            "allOf": [
              {
                "$ref": "#/components/schemas/WorkflowDefinitionBodyDto"
              }
            ]
          }
        },
        "required": [
          "id",
          "name",
          "active",
          "publishedVersion",
          "hasUnpublishedChanges",
          "documentsCount",
          "createdAt",
          "updatedAt",
          "definition"
        ]
      },
      "CreateWorkflowDto": {
        "type": "object",
        "properties": {
          "questions": {
            "description": "Perguntas feitas à pessoa antes dos documentos (até 30).",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WorkflowQuestionDto"
            }
          },
          "documents": {
            "description": "Documentos pedidos (até 50).",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WorkflowDocumentDto"
            }
          },
          "steps": {
            "description": "Etapas depois que a pessoa envia tudo, na ordem. A conclusão entra sozinha no fim.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WorkflowStepDto"
            }
          },
          "name": {
            "type": "string",
            "description": "Nome do workflow (aparece para a pessoa convidada).",
            "example": "Admissão de colaborador"
          },
          "description": {
            "type": "object",
            "description": "Descrição mostrada na página da pessoa convidada."
          },
          "publish": {
            "type": "boolean",
            "default": false,
            "description": "Publica a versão 1 na mesma chamada, conferindo se modelos e campos existem na conta. Se a publicação falhar, nada é criado."
          },
          "active": {
            "type": "boolean",
            "description": "Com `publish`, `false` deixa o workflow publicado mas inativo (sem convites). Padrão: ativo ao publicar."
          }
        },
        "required": [
          "name"
        ]
      },
      "UpdateWorkflowDto": {
        "type": "object",
        "properties": {
          "questions": {
            "description": "Perguntas feitas à pessoa antes dos documentos (até 30).",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WorkflowQuestionDto"
            }
          },
          "documents": {
            "description": "Documentos pedidos (até 50).",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WorkflowDocumentDto"
            }
          },
          "steps": {
            "description": "Etapas depois que a pessoa envia tudo, na ordem. A conclusão entra sozinha no fim.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WorkflowStepDto"
            }
          },
          "name": {
            "type": "string",
            "description": "Novo nome."
          },
          "description": {
            "type": "object",
            "description": "Nova descrição (`null` apaga)."
          },
          "publish": {
            "type": "boolean",
            "default": false,
            "description": "Publica o rascunho como nova versão depois de salvar. Processos em andamento seguem na versão em que começaram."
          },
          "active": {
            "type": "boolean",
            "description": "`true` ativa (exige versão publicada); `false` desativa: para de aceitar convites novos."
          }
        }
      },
      "CreateWorkflowInvitationDto": {
        "type": "object",
        "properties": {
          "recipientName": {
            "type": "string",
            "description": "Nome completo da pessoa, como está nos documentos (usado na regra de titularidade quando não há CPF).",
            "example": "Gabriela Ramos"
          },
          "recipientEmail": {
            "type": "string",
            "description": "E-mail da pessoa. Também identifica o convidado para reaproveitar em outros envios.",
            "example": "gabriela.ramos@example.com"
          },
          "recipientCpf": {
            "type": "object",
            "description": "CPF da pessoa, com ou sem máscara. Com CPF, a regra `BELONGS_TO_RECIPIENT` compara o número em vez do nome.",
            "example": "000.624.038-00"
          },
          "message": {
            "type": "object",
            "description": "Mensagem no e-mail e na página da pessoa.",
            "example": "Envie os documentos até sexta."
          },
          "expiresInDays": {
            "type": "number",
            "default": 7,
            "minimum": 0,
            "maximum": 90,
            "description": "Prazo para enviar, em dias a partir de agora; `0` = sem prazo."
          },
          "allowLate": {
            "type": "boolean",
            "default": false,
            "description": "`true`: depois do prazo o link continua aceitando e o processo fica `OVERDUE`. `false`: o link expira (`EXPIRED`)."
          },
          "sendEmail": {
            "type": "boolean",
            "default": true,
            "description": "`true` envia o convite por e-mail. `false` só gera o link (`LINK_CREATED`) para você enviar pelo seu canal."
          }
        },
        "required": [
          "recipientName",
          "recipientEmail"
        ]
      },
      "WorkflowInvitationCreatedDto": {
        "type": "object",
        "properties": {
          "processId": {
            "type": "string",
            "description": "Id do processo aberto (use em GET /workflow-processes/{id})."
          },
          "invitationId": {
            "type": "string",
            "description": "Id do convite."
          },
          "link": {
            "type": "string",
            "description": "Link pessoal da pessoa convidada. Quem tiver o link envia documentos em nome dela: não publique."
          },
          "emailSent": {
            "type": "boolean",
            "description": "O e-mail saiu."
          },
          "situation": {
            "type": "string",
            "enum": [
              "LINK_CREATED",
              "SENT",
              "IN_PROGRESS",
              "OVERDUE",
              "EXPIRED",
              "PENDING",
              "SUBMITTED_LATE",
              "FINISHED",
              "FINISHED_LATE",
              "REJECTED",
              "CANCELLED"
            ],
            "description": "`SENT` quando o e-mail saiu; `LINK_CREATED` quando só o link foi gerado."
          },
          "deadline": {
            "type": "object",
            "nullable": true,
            "description": "Prazo para enviar (ISO 8601); `null` = sem prazo."
          }
        },
        "required": [
          "processId",
          "invitationId",
          "link",
          "emailSent",
          "situation"
        ]
      },
      "WorkflowRefDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Id do workflow."
          },
          "name": {
            "type": "string",
            "description": "Nome do workflow."
          },
          "version": {
            "type": "number",
            "description": "Versão publicada em que este processo começou."
          }
        },
        "required": [
          "id",
          "name",
          "version"
        ]
      },
      "WorkflowProcessRecipientDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Nome da pessoa convidada."
          },
          "email": {
            "type": "string",
            "description": "E-mail da pessoa convidada."
          },
          "cpf": {
            "type": "object",
            "nullable": true,
            "description": "CPF informado no convite, mascarado (`***.***.***-00`) salvo `reveal=true`; `null` se não foi informado."
          }
        },
        "required": [
          "name",
          "email"
        ]
      },
      "WorkflowProcessStepDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Id da etapa."
          },
          "name": {
            "type": "string",
            "description": "Nome da etapa."
          },
          "type": {
            "type": "string",
            "enum": [
              "DOCUMENT_INPUT",
              "APPROVAL",
              "NOTIFICATION",
              "INTEGRATION",
              "COMPLETION"
            ],
            "description": "`DOCUMENT_INPUT` = coletando documentos."
          }
        },
        "required": [
          "id",
          "name",
          "type"
        ]
      },
      "WorkflowProcessRecurringDto": {
        "type": "object",
        "properties": {
          "dispatchId": {
            "type": "string",
            "description": "Id do envio recorrente que abriu o processo (criado pelo painel)."
          },
          "name": {
            "type": "object",
            "nullable": true,
            "description": "Nome do envio recorrente."
          },
          "cycleDueDate": {
            "type": "object",
            "nullable": true,
            "description": "Vencimento do ciclo (AAAA-MM-DD)."
          }
        },
        "required": [
          "dispatchId"
        ]
      },
      "WorkflowProcessAnswerDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Id da pergunta."
          },
          "label": {
            "type": "string",
            "description": "Texto da pergunta."
          },
          "answer": {
            "type": "object",
            "nullable": true,
            "description": "Resposta em texto (`\"true\"`, `\"2\"`…); `null` se ainda não respondida."
          }
        },
        "required": [
          "id",
          "label"
        ]
      },
      "WorkflowProcessIssueDto": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string",
            "example": "EXPIRED",
            "description": "Código da pendência (`EXPIRED`, `FIELD_MISSING`, `HOLDER_MISMATCH`, `MODEL_MISMATCH`…)."
          },
          "severity": {
            "type": "string",
            "enum": [
              "BLOCK",
              "REVIEW"
            ],
            "description": "`BLOCK`: a pessoa precisa enviar outro arquivo. `REVIEW`: aceito, para a sua equipe conferir."
          },
          "message": {
            "type": "string",
            "description": "Mensagem que a pessoa viu."
          },
          "detail": {
            "type": "object",
            "nullable": true,
            "description": "Detalhe para a sua equipe (ex.: a data lida)."
          }
        },
        "required": [
          "code",
          "severity",
          "message"
        ]
      },
      "WorkflowProcessFileDto": {
        "type": "object",
        "properties": {
          "documentId": {
            "type": "string",
            "description": "Id do documento no NouVy (também acessível em /documents/{id})."
          },
          "fileName": {
            "type": "object",
            "nullable": true,
            "description": "Nome do arquivo enviado."
          },
          "mimeType": {
            "type": "object",
            "nullable": true,
            "description": "Tipo do arquivo (`application/pdf`, `image/jpeg`…)."
          },
          "size": {
            "type": "object",
            "nullable": true,
            "description": "Tamanho em bytes."
          },
          "uploadedAt": {
            "format": "date-time",
            "type": "string",
            "description": "Quando a pessoa enviou (ISO 8601)."
          },
          "downloadUrl": {
            "type": "object",
            "nullable": true,
            "description": "Link assinado para baixar o arquivo. Vale até `downloadUrlExpiresAt`; para um link novo, consulte o processo de novo."
          },
          "downloadUrlExpiresAt": {
            "type": "object",
            "nullable": true,
            "description": "Validade do `downloadUrl` (ISO 8601)."
          }
        },
        "required": [
          "documentId",
          "uploadedAt"
        ]
      },
      "WorkflowProcessFieldDto": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string",
            "example": "validade",
            "description": "Chave do campo do modelo."
          },
          "label": {
            "type": "string",
            "example": "Validade",
            "description": "Nome do campo."
          },
          "type": {
            "type": "string",
            "example": "DATE",
            "description": "Tipo do campo (`TEXT`, `DATE`, `CPF`, `MONEY`…)."
          },
          "value": {
            "type": "object",
            "nullable": true,
            "description": "Valor lido em texto legível (datas em dd/mm/aaaa). CPF e números vêm mascarados salvo `reveal=true`."
          },
          "state": {
            "type": "object",
            "nullable": true,
            "description": "Quando não há valor: `ILLEGIBLE` (ilegível), `NOT_FOUND` (não consta)…; `null` quando há valor."
          }
        },
        "required": [
          "key",
          "label",
          "type"
        ]
      },
      "WorkflowProcessDataDto": {
        "type": "object",
        "properties": {
          "modelKey": {
            "type": "object",
            "nullable": true,
            "description": "Modelo em que o documento foi lido."
          },
          "modelName": {
            "type": "object",
            "nullable": true,
            "description": "Nome do modelo."
          },
          "fields": {
            "description": "Campos lidos (com valor ou com estado).",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WorkflowProcessFieldDto"
            }
          },
          "maskedFields": {
            "description": "Campos que saíram mascarados.",
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "fields",
          "maskedFields"
        ]
      },
      "WorkflowProcessDocumentDto": {
        "type": "object",
        "properties": {
          "slotId": {
            "type": "string",
            "description": "Id do documento pedido no workflow (`documents[].id`)."
          },
          "itemIndex": {
            "type": "number",
            "description": "Posição nos documentos repetidos (0, 1, 2… — um por filho, por exemplo). `0` nos demais."
          },
          "label": {
            "type": "string",
            "description": "Nome do documento (com o número da repetição, se houver)."
          },
          "holder": {
            "type": "string",
            "enum": [
              "TITULAR",
              "CONJUGE",
              "FILHO",
              "DEPENDENTE",
              "OUTRO"
            ],
            "description": "De quem é o documento."
          },
          "required": {
            "type": "boolean",
            "description": "Obrigatório para enviar."
          },
          "acceptedModels": {
            "description": "Modelos aceitos (nomes).",
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "status": {
            "type": "string",
            "enum": [
              "PENDING",
              "PROCESSING",
              "ACCEPTED",
              "RECEIVED",
              "REJECTED",
              "RESEND_REQUESTED"
            ],
            "description": "`PENDING`: não enviado. `PROCESSING`: em leitura. `ACCEPTED`: aceito. `RECEIVED`: aceito com pendência para a sua equipe. `REJECTED`: recusado, a pessoa precisa enviar outro. `RESEND_REQUESTED`: sua equipe pediu outro arquivo."
          },
          "reviewStatus": {
            "type": "object",
            "nullable": true,
            "description": "Estado interno da conferência (mais detalhado que `status`)."
          },
          "issues": {
            "description": "Pendências do arquivo atual.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WorkflowProcessIssueDto"
            }
          },
          "file": {
            "nullable": true,
            "description": "Arquivo enviado; `null` enquanto não foi enviado.",
            "allOf": [
              {
                "$ref": "#/components/schemas/WorkflowProcessFileDto"
              }
            ]
          },
          "data": {
            "nullable": true,
            "description": "Dados lidos do arquivo; `null` enquanto não há leitura.",
            "allOf": [
              {
                "$ref": "#/components/schemas/WorkflowProcessDataDto"
              }
            ]
          }
        },
        "required": [
          "slotId",
          "itemIndex",
          "label",
          "holder",
          "required",
          "acceptedModels",
          "status",
          "issues"
        ]
      },
      "WorkflowProcessDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Id do processo."
          },
          "invitationId": {
            "type": "object",
            "nullable": true,
            "description": "Id do convite que abriu o processo."
          },
          "workflow": {
            "description": "Workflow e versão do processo.",
            "allOf": [
              {
                "$ref": "#/components/schemas/WorkflowRefDto"
              }
            ]
          },
          "recipient": {
            "nullable": true,
            "description": "Pessoa convidada.",
            "allOf": [
              {
                "$ref": "#/components/schemas/WorkflowProcessRecipientDto"
              }
            ]
          },
          "situation": {
            "type": "string",
            "enum": [
              "LINK_CREATED",
              "SENT",
              "IN_PROGRESS",
              "OVERDUE",
              "EXPIRED",
              "PENDING",
              "SUBMITTED_LATE",
              "FINISHED",
              "FINISHED_LATE",
              "REJECTED",
              "CANCELLED"
            ],
            "description": "Situação do processo (tabela no guia Workflows)."
          },
          "situationLabel": {
            "type": "string",
            "example": "Pendente",
            "description": "Situação em português, pronta para exibir."
          },
          "status": {
            "type": "string",
            "description": "Estado interno (`WAITING_DOCUMENTS`, `WAITING_APPROVAL`, `WAITING_INTEGRATION`, `COMPLETED`, `REJECTED`, `ERROR`, `CANCELLED`)."
          },
          "currentStep": {
            "nullable": true,
            "description": "Etapa em que o processo está.",
            "allOf": [
              {
                "$ref": "#/components/schemas/WorkflowProcessStepDto"
              }
            ]
          },
          "createdAt": {
            "format": "date-time",
            "type": "string",
            "description": "Abertura do processo (ISO 8601)."
          },
          "sentAt": {
            "type": "object",
            "nullable": true,
            "description": "Último envio do convite por e-mail; `null` se só o link foi gerado."
          },
          "openedAt": {
            "type": "object",
            "nullable": true,
            "description": "Primeira vez que a pessoa abriu o link."
          },
          "submittedAt": {
            "type": "object",
            "nullable": true,
            "description": "Quando a pessoa enviou tudo para análise."
          },
          "completedAt": {
            "type": "object",
            "nullable": true,
            "description": "Conclusão, reprovação ou cancelamento."
          },
          "deadline": {
            "type": "object",
            "nullable": true,
            "description": "Prazo para enviar; `null` = sem prazo."
          },
          "allowLate": {
            "type": "boolean",
            "description": "O link aceita envio depois do prazo."
          },
          "submittedLate": {
            "type": "object",
            "nullable": true,
            "description": "A primeira entrega foi depois do prazo; `null` enquanto não houve entrega."
          },
          "recurring": {
            "nullable": true,
            "description": "Envio recorrente de origem; `null` em convite único.",
            "allOf": [
              {
                "$ref": "#/components/schemas/WorkflowProcessRecurringDto"
              }
            ]
          },
          "answers": {
            "description": "Respostas às perguntas.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WorkflowProcessAnswerDto"
            }
          },
          "documents": {
            "description": "Todos os documentos pedidos à pessoa (segundo as respostas); os não enviados com `file` e `data` nulos.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WorkflowProcessDocumentDto"
            }
          }
        },
        "required": [
          "id",
          "workflow",
          "situation",
          "situationLabel",
          "status",
          "createdAt",
          "allowLate",
          "answers",
          "documents"
        ]
      },
      "WorkflowProcessListResponseDto": {
        "type": "object",
        "properties": {
          "items": {
            "description": "Processos da página, mais novos primeiro.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WorkflowProcessDto"
            }
          },
          "pagination": {
            "description": "Paginação.",
            "allOf": [
              {
                "$ref": "#/components/schemas/PaginationMetaDto"
              }
            ]
          }
        },
        "required": [
          "items",
          "pagination"
        ]
      },
      "AssistantQuestionScopeDto": {
        "type": "object",
        "properties": {
          "documentIds": {
            "description": "Documentos que a pergunta pode usar (até 200).",
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          },
          "taxIds": {
            "description": "CPF ou CNPJ (com ou sem pontuação, até 20): documentos em que a pessoa ou empresa aparece como parte (emitente, tomador, contratante…).",
            "example": [
              "12345678000190"
            ],
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "workflowInstanceId": {
            "type": "string",
            "description": "Processo de workflow: os documentos aceitos que a pessoa enviou.",
            "format": "uuid"
          }
        }
      },
      "AskAssistantQuestionDto": {
        "type": "object",
        "properties": {
          "question": {
            "type": "string",
            "description": "A pergunta, em linguagem natural.",
            "example": "Qual o valor total das notas de setembro?"
          },
          "externalUserId": {
            "type": "string",
            "description": "Quem pergunta no seu sistema (id opaco, até 128 caracteres). Vai para auditoria, limites por usuário e para a conversa: só o mesmo usuário, com o mesmo escopo, continua uma conversa.",
            "example": "cliente-8812"
          },
          "conversationId": {
            "type": "string",
            "description": "Continua uma conversa desta mesma combinação de usuário e escopo.",
            "format": "uuid"
          },
          "scope": {
            "description": "Obrigatório. Os documentos que a resposta pode usar. Âncoras combinam por interseção (ex.: CNPJ **e** processo). Nunca é o acervo inteiro.",
            "allOf": [
              {
                "$ref": "#/components/schemas/AssistantQuestionScopeDto"
              }
            ]
          }
        },
        "required": [
          "question",
          "externalUserId",
          "scope"
        ]
      },
      "AssistantAnswerSourceDto": {
        "type": "object",
        "properties": {
          "documentId": {
            "type": "string",
            "format": "uuid"
          },
          "title": {
            "type": "string",
            "description": "Título ou nome do arquivo."
          },
          "page": {
            "type": "object",
            "nullable": true
          },
          "excerpt": {
            "type": "string",
            "description": "Trecho usado na resposta (até 500 caracteres)."
          }
        },
        "required": [
          "documentId"
        ]
      },
      "AssistantAnswerDto": {
        "type": "object",
        "properties": {
          "conversationId": {
            "type": "string",
            "format": "uuid"
          },
          "answer": {
            "type": "string"
          },
          "found": {
            "type": "boolean",
            "description": "false quando a resposta não está nos documentos do escopo."
          },
          "sources": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AssistantAnswerSourceDto"
            }
          },
          "scope": {
            "type": "object",
            "example": {
              "documentCount": 12
            }
          },
          "chargedBrl": {
            "type": "object",
            "description": "Valor debitado da carteira por esta pergunta, em reais. 0 quando não cobra (ex.: não encontrou).",
            "nullable": true,
            "example": 0.011
          }
        },
        "required": [
          "conversationId",
          "answer",
          "found",
          "sources",
          "scope",
          "chargedBrl"
        ]
      },
      "CreateAssistantSessionDto": {
        "type": "object",
        "properties": {
          "externalUserId": {
            "type": "string",
            "description": "Quem vai conversar, no seu sistema (id opaco, até 128 caracteres). A sessão fica presa a ele.",
            "example": "cliente-8812"
          },
          "scope": {
            "description": "Os documentos que a sessão pode usar, com as mesmas regras de `POST /assistant/questions`. Fica gravado na sessão: o navegador não consegue mudar nem ampliar.",
            "allOf": [
              {
                "$ref": "#/components/schemas/AssistantQuestionScopeDto"
              }
            ]
          },
          "ttlMinutes": {
            "type": "number",
            "description": "Duração da sessão, em minutos (5 a 60).",
            "default": 30,
            "minimum": 5,
            "maximum": 60
          }
        },
        "required": [
          "externalUserId",
          "scope"
        ]
      },
      "AssistantSessionDto": {
        "type": "object",
        "properties": {
          "sessionToken": {
            "type": "string",
            "description": "Token da sessão (`nvy_ses_…`). Entregue ao navegador do usuário; ele não dá acesso a outra rota da API."
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time"
          },
          "documentCount": {
            "type": "number",
            "description": "Quantos documentos o escopo encontrou."
          }
        },
        "required": [
          "sessionToken",
          "expiresAt",
          "documentCount"
        ]
      },
      "AssistantSessionQuestionDto": {
        "type": "object",
        "properties": {
          "question": {
            "type": "string",
            "description": "A pergunta, em linguagem natural.",
            "example": "Qual o vencimento do meu boleto?"
          },
          "conversationId": {
            "type": "string",
            "description": "Continua uma conversa desta sessão (mesmo usuário e escopo).",
            "format": "uuid"
          }
        },
        "required": [
          "question"
        ]
      },
      "AssistantSessionAnswerDto": {
        "type": "object",
        "properties": {
          "conversationId": {
            "type": "string",
            "format": "uuid"
          },
          "answer": {
            "type": "string"
          },
          "found": {
            "type": "boolean",
            "description": "false quando a resposta não está nos documentos do escopo."
          },
          "sources": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AssistantAnswerSourceDto"
            }
          },
          "scope": {
            "type": "object",
            "example": {
              "documentCount": 12
            }
          }
        },
        "required": [
          "conversationId",
          "answer",
          "found",
          "sources",
          "scope"
        ]
      }
    }
  }
}