{
  "openapi": "3.1.0",
  "info": {
    "title": "JEVaaS — Decisão tipada como serviço",
    "version": "0.1.0",
    "description": "Julgamento tipado, roteado por confiança e auditável. Um **contrato versionado** declara as perguntas, as opções e as rotas; o serviço devolve uma resposta tipada (`choice`/`score`/`noul`), a rota, o limiar aplicado e um **recibo**. Especificação escrita à mão a partir de `devkit/API-CONTRACT.md` — se este arquivo divergir do contrato, vale o contrato.\n\n**Autenticação:** `Authorization: Bearer jev_sk_*` em toda rota de dados. O inquilino é derivado da chave: `tenant_id` no corpo é ignorado. Escopos `read` | `write` | `admin` (admin cobre tudo, write cobre read).\n\n**Invariante:** o serviço **nunca executa nada**. Não existe campo de execução na resposta: `allow` é um token opaco e quem o mapeia para permissão é o motor de política do consumidor. Em `mode: shadow` o julgamento vem com `enforced: false` — mede, não autoriza.\n\n**Erros:** toda resposta >= 400 traz `{error, code, request_id}` e o header `X-Request-Id`. `429` traz `Retry-After`.\n\n**Versionamento:** publicar mudança é publicar **versão nova** (a anterior fica imutável, para poder reverter). O `fingerprint` identifica o conteúdo decisório que julgou cada recibo; republicar a mesma versão com conteúdo diferente devolve `409`."
  },
  "servers": [
    {
      "url": "https://api.jev.vertikon.com.br/v1",
      "description": "Borda de produção da vertical JEVaaS — host CANÔNICO, o padrão dos SDKs. O `/v1` é a VERSÃO da API (não um prefixo de conveniência): o nginx o remove antes do proxy, então os `paths` abaixo são os do serviço, não os do fio. É o mesmo serviço do segundo servidor; trocar de host não muda caminho nenhum."
    },
    {
      "url": "https://jev.vertikon.com.br/v1",
      "description": "Mesma borda no host do produto, mantida para não quebrar consumidor já apontado para ela (há recibo de produção medido por aqui). Prefira o host `api.` em código novo."
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "decisões",
      "description": "Julgar um estado: com contrato publicado (`/decisions/judge`) ou ad-hoc (`/decisions/fanout`)"
    },
    {
      "name": "contratos",
      "description": "Documentos versionados que declaram perguntas, rotas e limiares"
    },
    {
      "name": "recibos",
      "description": "Trilha auditável de cada julgamento, com rótulo humano e desfecho de produção"
    },
    {
      "name": "calibração",
      "description": "Acerto × confiança por faixa, divergência em sombra e golden set"
    },
    {
      "name": "conta",
      "description": "Identidade da chave, quota e consumo"
    },
    {
      "name": "docs",
      "description": "Saúde e documentação da própria borda"
    }
  ],
  "paths": {
    "/decisions/judge": {
      "post": {
        "tags": [
          "decisões"
        ],
        "operationId": "judgeDecision",
        "summary": "Julga um estado contra um contrato publicado",
        "description": "Julga `state` com as perguntas do contrato (a versão vigente, ou a fixada em `contract_version`) e devolve a decisão roteada + o recibo auditável.\n\nO roteamento é o resultado do julgamento: a **primeira** regra de `routes` que casar vence; se nenhuma casar, vale `default_route`. `route: auto` só aparece com confiança acima da barra da classe da ação — e é o único caso em que `allow` vem preenchido.\n\nCada rodada gasta tokens do inquilino: `usage` e `cost_micros_usd` no recibo são a contabilidade, e o teto diário devolve `429 budget_exceeded`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/JudgeRequest"
              },
              "examples": {
                "ticket-router": {
                  "summary": "Triagem de ticket de suporte (contracts/ticket-router.v3.yaml)",
                  "value": {
                    "contract": "ticket-router",
                    "contract_version": 3,
                    "state": {
                      "ticket": {
                        "messages": [
                          {
                            "author": "customer",
                            "text": "O relatório de notas saiu com a coluna de média zerada. Preciso disso ainda hoje."
                          }
                        ]
                      },
                      "options": {
                        "queues": {
                          "vendas": "Comercial",
                          "suporte": "Suporte técnico",
                          "financeiro": "Financeiro"
                        }
                      }
                    },
                    "state_version": "run_184:step_7",
                    "operation": "support.triage",
                    "decision_id": "tkt_9931",
                    "trace_id": "trc_7f1c9a"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Julgamento válido. O corpo é o recibo completo (§4.1).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JudgeResponse"
                },
                "examples": {
                  "fila-suporte": {
                    "summary": "fila_principal == suporte com confiança acima da barra → rota auto",
                    "value": {
                      "receipt_id": "rcp_01J9Z7Q2K8V4M3",
                      "contract": {
                        "id": "ticket-router",
                        "version": 3,
                        "ref": "ticket-router@3",
                        "fingerprint": "b4761c0f9d2a77e5c1a0b3f4e6d8a9c2",
                        "mode": "enforce",
                        "action_class": "internal_write"
                      },
                      "status": "decided",
                      "route": "auto",
                      "allow": "support:route",
                      "enforced": true,
                      "selected": {
                        "question_id": "fila_principal",
                        "type": "choice",
                        "choice": "suporte",
                        "probabilities": {
                          "vendas": 0.04,
                          "suporte": 0.93,
                          "financeiro": 0.01,
                          "indeterminado": 0.02
                        },
                        "confidence": 0.93,
                        "escape_hatch": false
                      },
                      "decisive_confidence": 0.93,
                      "answers": [
                        {
                          "question_id": "fila_principal",
                          "type": "choice",
                          "choice": "suporte",
                          "probabilities": {
                            "vendas": 0.04,
                            "suporte": 0.93,
                            "financeiro": 0.01,
                            "indeterminado": 0.02
                          },
                          "confidence": 0.93,
                          "escape_hatch": false
                        },
                        {
                          "question_id": "pede_humano",
                          "type": "noul",
                          "noul": 0.12,
                          "escape_hatch": false
                        },
                        {
                          "question_id": "urgencia",
                          "type": "score",
                          "score": 2,
                          "legend": {
                            "0": "Não informa prazo.",
                            "1": "Solicita em breve.",
                            "2": "Solicita imediata."
                          },
                          "probabilities": {
                            "0": 0.03,
                            "1": 0.07,
                            "2": 0.9
                          },
                          "confidence": 0.88,
                          "escape_hatch": false
                        }
                      ],
                      "threshold": {
                        "action_class": "internal_write",
                        "bar": 0.75,
                        "collect_floor": 0.7,
                        "matched_rule": 1,
                        "reason": "fila_principal ==suporte"
                      },
                      "escalation": null,
                      "warnings": [],
                      "injected_options": [],
                      "improvement_hint": null,
                      "usage": {
                        "input_tokens": 954,
                        "output_tokens": 0
                      },
                      "cost_micros_usd": 40,
                      "latency_ms": 1630,
                      "state_hash": "sha256:9f2c41ab77e5c1a0b3f4e6d8a9c2b4761c0f9d2a77e5c1a0b3f4e6d8a9c2b476",
                      "model_requested": "typesafe/jev-latest",
                      "model_real": "jev-1.13.0",
                      "key_source": "vertikon_pool",
                      "request_id": "req_f61b9f0ae4876c5f2df4ab9a"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/E400"
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          },
          "422": {
            "$ref": "#/components/responses/E422"
          },
          "429": {
            "$ref": "#/components/responses/E429"
          },
          "502": {
            "$ref": "#/components/responses/E502"
          },
          "504": {
            "$ref": "#/components/responses/E504"
          }
        }
      }
    },
    "/decisions/fanout": {
      "post": {
        "tags": [
          "decisões"
        ],
        "operationId": "fanoutDecision",
        "summary": "Julga perguntas soltas, sem contrato publicado",
        "description": "Ad-hoc: as `questions` vão inline no lugar do `contract`. Sem rotas, sem barra e sem recibo — devolve **só** as respostas tipadas. `status: decided` aqui **não** autoriza nada: é julgamento cru, para uso interno do consumidor.\n\nUse `judge` quando a decisão governa produção; `fanout` é para perguntas exploratórias e classificação intermediária.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FanoutRequest"
              },
              "examples": {
                "urgencia": {
                  "summary": "Duas perguntas soltas sobre um relato",
                  "value": {
                    "state": {
                      "ticket": {
                        "messages": [
                          {
                            "author": "customer",
                            "text": "Preciso disso ainda hoje."
                          }
                        ]
                      }
                    },
                    "questions": {
                      "is_urgent": {
                        "type": "noul",
                        "instructions": "O cliente pede resolução imediata?"
                      },
                      "urgencia": {
                        "type": "score",
                        "instructions": "Qual urgência de resolução é expressa?",
                        "criteria": [
                          "Não informa prazo.",
                          "Solicita em breve.",
                          "Solicita imediata."
                        ]
                      }
                    },
                    "model": "typesafe/jev-latest"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Respostas tipadas, sem rota e sem autorização.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FanoutResponse"
                },
                "examples": {
                  "duas-perguntas": {
                    "value": {
                      "status": "decided",
                      "answers": [
                        {
                          "question_id": "is_urgent",
                          "type": "noul",
                          "noul": 0.91,
                          "escape_hatch": false
                        },
                        {
                          "question_id": "urgencia",
                          "type": "score",
                          "score": 2,
                          "legend": {
                            "0": "Não informa prazo.",
                            "1": "Solicita em breve.",
                            "2": "Solicita imediata."
                          },
                          "probabilities": {
                            "0": 0.02,
                            "1": 0.05,
                            "2": 0.93
                          },
                          "confidence": 0.9,
                          "escape_hatch": false
                        }
                      ],
                      "usage": {
                        "input_tokens": 412,
                        "output_tokens": 0
                      },
                      "latency_ms": 900,
                      "request_id": "req_3c1a8f0b9d2e4a67bc15de93"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/E400"
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "422": {
            "$ref": "#/components/responses/E422"
          },
          "429": {
            "$ref": "#/components/responses/E429"
          },
          "502": {
            "$ref": "#/components/responses/E502"
          },
          "504": {
            "$ref": "#/components/responses/E504"
          }
        }
      }
    },
    "/contracts": {
      "get": {
        "tags": [
          "contratos"
        ],
        "operationId": "listContracts",
        "summary": "Lista os contratos do inquilino (última versão de cada)",
        "description": "A chave enxerga apenas os contratos do próprio inquilino.",
        "responses": {
          "200": {
            "description": "Array de documentos de contrato (§4.7).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/DecisionContract"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "429": {
            "$ref": "#/components/responses/E429"
          }
        }
      },
      "post": {
        "tags": [
          "contratos"
        ],
        "operationId": "createContract",
        "summary": "Publica a versão 1 de um contrato",
        "description": "Valida o documento antes de gravar (§5: saída de escape, níveis descritivos, `default_route` diferente de `auto`, limiar compatível com a primitiva, `collect_floor` <= `bar`). Contrato reprovado não é gravado: devolve `422 invalid_contract` com a razão.\n\n`version`, `fingerprint`, `created_at` e `updated_at` são **acrescentados pelo serviço** (§3.4).",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ContractInput"
              },
              "examples": {
                "ticket-router": {
                  "summary": "Contrato mínimo do ticket-router",
                  "value": {
                    "id": "ticket-router",
                    "description": "Roteia o ticket para a fila que deve tratá-lo.",
                    "owner": "suporte@vertikon.com.br",
                    "mode": "enforce",
                    "action_class": "internal_write",
                    "bar": 0.75,
                    "collect_floor": 0.7,
                    "primary_question": "fila_principal",
                    "default_route": "collect_evidence",
                    "escalations": {
                      "human_review": "fila-suporte-nivel-2",
                      "default": "fila-triagem-humana"
                    },
                    "state_fields": [
                      {
                        "path": "ticket.messages",
                        "role": "evidence",
                        "required": true
                      },
                      {
                        "path": "options.queues",
                        "role": "option",
                        "required": false
                      }
                    ],
                    "questions": {
                      "fila_principal": {
                        "type": "choice",
                        "round": 0,
                        "instructions": "Qual equipe deve tratar a solicitação principal do cliente em ticket.messages?",
                        "criteria": {
                          "vendas": "Solicitação de demonstração, contratação ou ampliação do serviço.",
                          "suporte": "Falha de funcionamento, configuração ou integração.",
                          "financeiro": "Questão sobre fatura, cobrança ou pagamento.",
                          "indeterminado": "Evidência insuficiente, nenhuma categoria adequada ou conflito sem prioridade clara."
                        }
                      },
                      "pede_humano": {
                        "type": "noul",
                        "instructions": "O cliente pede atendimento por uma pessoa em ticket.messages?"
                      },
                      "urgencia": {
                        "type": "score",
                        "instructions": "Qual urgência de resolução é expressa pelo cliente em ticket.messages?",
                        "criteria": [
                          "Não informa necessidade de prazo ou urgência.",
                          "Solicita resolução em breve, sem afirmar necessidade imediata ou no mesmo dia.",
                          "Solicita resolução imediata, no mesmo dia ou antes de prazo iminente explícito."
                        ]
                      }
                    },
                    "routes": [
                      {
                        "when": {
                          "question": "fila_principal",
                          "equals": "indeterminado",
                          "escape_hatch": true
                        },
                        "route": "abstain",
                        "reason": "saída de escape venceu: o menu de filas é o problema"
                      },
                      {
                        "when": {
                          "question": "fila_principal",
                          "equals": "suporte",
                          "confidence_at_least": 0.75
                        },
                        "route": "auto",
                        "allow": "support:route",
                        "reason": "fila_principal ==suporte"
                      },
                      {
                        "when": {
                          "question": "pede_humano",
                          "noul_at_least": 0.6
                        },
                        "route": "human_review",
                        "reason": "cliente pede atendimento por uma pessoa"
                      },
                      {
                        "when": {
                          "question": "urgencia",
                          "score_at_least": 2
                        },
                        "route": "human_review",
                        "reason": "urgência imediata declarada"
                      },
                      {
                        "when": {
                          "question": "fila_principal",
                          "confidence_below": 0.7
                        },
                        "route": "human_review",
                        "reason": "confiança abaixo do piso (0,70)"
                      }
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Versão 1 gravada e devolvida com `version`, `fingerprint`, `created_at` e `updated_at`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DecisionContract"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/E400"
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "409": {
            "$ref": "#/components/responses/E409"
          },
          "422": {
            "$ref": "#/components/responses/E422"
          },
          "429": {
            "$ref": "#/components/responses/E429"
          }
        }
      }
    },
    "/contracts/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ContractId"
        }
      ],
      "get": {
        "tags": [
          "contratos"
        ],
        "operationId": "getContract",
        "summary": "Última versão do contrato",
        "description": "Devolve o documento vigente no inquilino. Contrato de outro inquilino é `404 contract_not_found` — a existência de um contrato alheio não é revelada.",
        "responses": {
          "200": {
            "description": "Um documento de contrato (§4.7).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DecisionContract"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          },
          "429": {
            "$ref": "#/components/responses/E429"
          }
        }
      },
      "put": {
        "tags": [
          "contratos"
        ],
        "operationId": "updateContract",
        "summary": "Publica uma nova versão do contrato",
        "description": "**Nenhuma versão é editada no lugar:** publicar mudança é criar versão nova, para poder reverter. A versão anterior permanece imutável e continua sendo a que julgou os recibos já emitidos.\n\nO corpo é o documento completo da nova versão, e o `id` deve casar com o `{id}` do caminho. Publicar conteúdo decisório idêntico (mesmo `fingerprint`) na mesma versão devolve `409 version_conflict`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ContractInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Nova versão gravada e devolvida (a anterior segue consultável em `/contracts/{id}/versions/{v}`).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DecisionContract"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/E400"
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          },
          "409": {
            "$ref": "#/components/responses/E409"
          },
          "422": {
            "$ref": "#/components/responses/E422"
          },
          "429": {
            "$ref": "#/components/responses/E429"
          }
        }
      }
    },
    "/contracts/{id}/versions": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ContractId"
        }
      ],
      "get": {
        "tags": [
          "contratos"
        ],
        "operationId": "listContractVersions",
        "summary": "Histórico de versões do contrato",
        "description": "Todas as versões publicadas, da mais antiga à mais nova. É o insumo da reversão: promover uma versão antiga não apaga o histórico.",
        "responses": {
          "200": {
            "description": "Array de documentos de contrato (§4.7).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/DecisionContract"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          },
          "429": {
            "$ref": "#/components/responses/E429"
          }
        }
      }
    },
    "/contracts/{id}/versions/{v}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ContractId"
        },
        {
          "$ref": "#/components/parameters/ContractVersionNumber"
        }
      ],
      "get": {
        "tags": [
          "contratos"
        ],
        "operationId": "getContractVersion",
        "summary": "Uma versão específica do contrato",
        "description": "Serve para reconstruir o julgamento de um recibo antigo: o `fingerprint` do recibo aponta para o conteúdo decisório exato que o produziu.",
        "responses": {
          "200": {
            "description": "O documento daquela versão (§4.7).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DecisionContract"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          },
          "429": {
            "$ref": "#/components/responses/E429"
          }
        }
      }
    },
    "/contracts/{id}/versions/{v}/promote": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ContractId"
        },
        {
          "$ref": "#/components/parameters/ContractVersionNumber"
        }
      ],
      "post": {
        "tags": [
          "contratos"
        ],
        "operationId": "promoteContractVersion",
        "summary": "Torna uma versão a vigente (escopo admin)",
        "description": "Reverter é promover a versão anterior: como nenhuma versão é editada no lugar, o rollback não perde o conteúdo nem a trilha. Exige escopo `admin` — mudar qual contrato governa produção é ato administrativo, não operação de rotina.",
        "responses": {
          "200": {
            "description": "O documento do contrato com a versão promovida agora como vigente.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DecisionContract"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          },
          "429": {
            "$ref": "#/components/responses/E429"
          }
        }
      }
    },
    "/contracts/{id}/mode": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ContractId"
        }
      ],
      "post": {
        "tags": [
          "contratos"
        ],
        "operationId": "setContractMode",
        "summary": "Troca o modo do contrato entre sombra e enforce (escopo admin)",
        "description": "`shadow` julga e mede sem autorizar (`enforced: false` no recibo); `enforce` autoriza a ação nas rotas `auto`. O caminho recomendado é começar em sombra, comparar a resposta de produção com o julgamento e só então promover.\n\nExige escopo `admin`: é o interruptor que decide se o serviço pode autorizar ação.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ModeRequest"
              },
              "examples": {
                "sombra": {
                  "value": {
                    "mode": "shadow"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "O documento do contrato com o novo `mode`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DecisionContract"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/E400"
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          },
          "422": {
            "$ref": "#/components/responses/E422"
          },
          "429": {
            "$ref": "#/components/responses/E429"
          }
        }
      }
    },
    "/receipts": {
      "get": {
        "tags": [
          "recibos"
        ],
        "operationId": "listReceipts",
        "summary": "Lista recibos do inquilino",
        "description": "Trilha auditável dos julgamentos. A paginação é por cursor opaco: o `cursor` volta no corpo e na última página vem ausente — não invente offset a partir do total.",
        "parameters": [
          {
            "name": "contract",
            "in": "query",
            "required": false,
            "description": "Filtra por id de contrato.",
            "schema": {
              "type": "string",
              "example": "ticket-router"
            }
          },
          {
            "name": "route",
            "in": "query",
            "required": false,
            "description": "Filtra pela rota decidida.",
            "schema": {
              "$ref": "#/components/schemas/Route"
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Filtra pelo status do julgamento (§3.3).",
            "schema": {
              "$ref": "#/components/schemas/DecisionStatus"
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Início do período (data ou timestamp RFC 3339).",
            "schema": {
              "type": "string",
              "example": "2026-09-01"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Fim do período (inclusive).",
            "schema": {
              "type": "string",
              "example": "2026-09-21"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Tamanho da página.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "example": 50
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "Cursor opaco devolvido pela página anterior.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Envelope com cursor opaco para paginação (§4.7).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ReceiptList"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/E400"
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "429": {
            "$ref": "#/components/responses/E429"
          }
        }
      }
    },
    "/receipts/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ReceiptId"
        }
      ],
      "get": {
        "tags": [
          "recibos"
        ],
        "operationId": "getReceipt",
        "summary": "Recibo completo",
        "description": "Distribuição inteira de probabilidades, limiar aplicado, rota, custo e o hash do estado julgado. É a peça que responde \"por que o sistema agiu assim?\" meses depois.",
        "responses": {
          "200": {
            "description": "Um recibo (§4.1).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Receipt"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          },
          "429": {
            "$ref": "#/components/responses/E429"
          }
        }
      }
    },
    "/receipts/{id}/label": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ReceiptId"
        }
      ],
      "post": {
        "tags": [
          "recibos"
        ],
        "operationId": "labelReceipt",
        "summary": "Anexa o rótulo humano ao recibo",
        "description": "O rótulo é o insumo da calibração (§4.5): sem ele a curva de acerto × confiança não existe, e sem a curva não há como decidir onde automatizar. `labeled_by` guarda quem rotulou.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LabelRequest"
              },
              "examples": {
                "suporte": {
                  "value": {
                    "human_label": "suporte",
                    "labeled_by": "ana@vertikon.com.br"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Rótulo gravado. Devolve o recibo atualizado.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Receipt"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/E400"
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          },
          "429": {
            "$ref": "#/components/responses/E429"
          }
        }
      }
    },
    "/receipts/{id}/outcome": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ReceiptId"
        }
      ],
      "post": {
        "tags": [
          "recibos"
        ],
        "operationId": "recordReceiptOutcome",
        "summary": "Devolve o desfecho de produção (modo sombra)",
        "description": "Em `mode: shadow` o consumidor **não** está autorizado a agir: ele julga por conta própria e devolve aqui qual foi a resposta de produção e se agiu. É esse par que a taxa de divergência da calibração compara com o julgamento do serviço.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OutcomeRequest"
              },
              "examples": {
                "divergiu": {
                  "value": {
                    "production_answer": "financeiro",
                    "action_taken": false
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Desfecho gravado. Devolve o recibo atualizado.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Receipt"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/E400"
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          },
          "429": {
            "$ref": "#/components/responses/E429"
          }
        }
      }
    },
    "/calibration/{contract}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ContractPathName"
        }
      ],
      "get": {
        "tags": [
          "calibração"
        ],
        "operationId": "getCalibration",
        "summary": "Acerto × confiança por faixa, divergência em sombra e deriva",
        "description": "A pergunta que esta rota responde não é \"a resposta média está certa?\" e sim \"as respostas de **confiança alta** são de fato mais confiáveis nesta carga?\" — é isso que decide onde automatizar. Por isso o acerto vem quebrado por faixa de confiança, e não só no agregado.\n\n`divergence` compara o julgamento com a resposta de produção devolvida em sombra (§4.4); `human_review_rate` mostra quanto do volume ainda para em pessoa; `drift` põe a acurácia versão a versão para revelar quando uma publicação piorou a carga real.",
        "responses": {
          "200": {
            "description": "Relatório de calibração (§4.7).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CalibrationReport"
                },
                "examples": {
                  "ticket-router": {
                    "value": {
                      "contract": "ticket-router",
                      "version": 3,
                      "mode": "enforce",
                      "total": 412,
                      "labeled": 96,
                      "correct": 88,
                      "accuracy": 0.917,
                      "by_band": [
                        {
                          "band": "0.90-1.00",
                          "total": 61,
                          "labeled": 40,
                          "correct": 39,
                          "accuracy": 0.975
                        },
                        {
                          "band": "0.75-0.90",
                          "total": 122,
                          "labeled": 38,
                          "correct": 34,
                          "accuracy": 0.895
                        },
                        {
                          "band": "0.70-0.75",
                          "total": 88,
                          "labeled": 12,
                          "correct": 10,
                          "accuracy": 0.833
                        },
                        {
                          "band": "0.00-0.70",
                          "total": 141,
                          "labeled": 6,
                          "correct": 5,
                          "accuracy": 0.833
                        }
                      ],
                      "routes": {
                        "auto": 240,
                        "collect_evidence": 96,
                        "human_review": 62,
                        "abstain": 14
                      },
                      "divergence": {
                        "shadow_total": 120,
                        "diverged": 17,
                        "rate": 0.142
                      },
                      "human_review_rate": 0.15,
                      "drift": [
                        {
                          "version": 2,
                          "accuracy": 0.88
                        },
                        {
                          "version": 3,
                          "accuracy": 0.917
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          },
          "429": {
            "$ref": "#/components/responses/E429"
          }
        }
      }
    },
    "/evals/{contract}/run": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ContractPathName"
        }
      ],
      "post": {
        "tags": [
          "calibração"
        ],
        "operationId": "runContractEval",
        "summary": "Roda o golden set do contrato (escopo admin)",
        "description": "Executa os casos rotulados do contrato e devolve acerto por faixa e a lista de falhas com o que era esperado, o que veio e com que confiança. É o gate de qualidade antes de promover uma versão — exige escopo `admin` porque gasta tokens do inquilino.\n\nO corpo é opcional: sem ele, a execução usa a versão vigente do contrato.",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "Corpo opcional, sem campos obrigatórios."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Resultado da execução do golden set (§4.7).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EvalReport"
                },
                "examples": {
                  "ticket-router": {
                    "value": {
                      "contract": "ticket-router",
                      "version": 3,
                      "total": 30,
                      "correct": 28,
                      "accuracy": 0.933,
                      "by_band": [
                        {
                          "band": "0.90-1.00",
                          "total": 12,
                          "labeled": 12,
                          "correct": 12,
                          "accuracy": 1
                        },
                        {
                          "band": "0.75-0.90",
                          "total": 11,
                          "labeled": 11,
                          "correct": 10,
                          "accuracy": 0.909
                        },
                        {
                          "band": "0.70-0.75",
                          "total": 4,
                          "labeled": 4,
                          "correct": 3,
                          "accuracy": 0.75
                        },
                        {
                          "band": "0.00-0.70",
                          "total": 3,
                          "labeled": 3,
                          "correct": 3,
                          "accuracy": 1
                        }
                      ],
                      "failures": [
                        {
                          "case": "ticket-17",
                          "expected": "suporte",
                          "got": "financeiro",
                          "confidence": 0.71
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/E400"
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          },
          "422": {
            "$ref": "#/components/responses/E422"
          },
          "429": {
            "$ref": "#/components/responses/E429"
          },
          "502": {
            "$ref": "#/components/responses/E502"
          },
          "504": {
            "$ref": "#/components/responses/E504"
          }
        }
      }
    },
    "/health": {
      "get": {
        "tags": [
          "docs"
        ],
        "operationId": "health",
        "summary": "Saúde do serviço (sem autenticação)",
        "description": "Usado pelo smoke test do deploy. É a única rota, junto com `/openapi.json` e `/docs`, que não exige chave.",
        "security": [],
        "responses": {
          "200": {
            "description": "Serviço no ar. O status HTTP é o sinal; o corpo é informativo.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Corpo informativo."
                }
              }
            }
          }
        }
      }
    },
    "/auth/me": {
      "get": {
        "tags": [
          "conta"
        ],
        "operationId": "getAccount",
        "summary": "Identidade da chave",
        "description": "Diz de qual inquilino é a chave, quais escopos ela tem e qual o plano. Serve para o consumidor se auto-diagnosticar antes de culpar a rota: `403 forbidden` costuma ser escopo `read` tentando escrever.",
        "responses": {
          "200": {
            "description": "Dados da chave (§4.7). O texto claro da chave nunca é devolvido — só `key_prefix`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountInfo"
                },
                "examples": {
                  "leitura-e-escrita": {
                    "value": {
                      "tenant_id": "eduuo",
                      "key_id": "key_7f1c9a2b",
                      "key_prefix": "jev_sk_…",
                      "scopes": [
                        "read",
                        "write"
                      ],
                      "plan": "standard",
                      "created_at": "2026-09-21T12:00:00Z"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "429": {
            "$ref": "#/components/responses/E429"
          }
        }
      }
    },
    "/quota": {
      "get": {
        "tags": [
          "conta"
        ],
        "operationId": "getQuota",
        "summary": "Teto e consumo do período",
        "description": "O teto é por inquilino e por período: estourou, a próxima decisão devolve `429 quota_exceeded` com `Retry-After`. Consulte aqui antes de subir volume — o teto de tokens é o que impede um laço de agente de queimar o orçamento da vertical.",
        "responses": {
          "200": {
            "description": "Quota do período (§4.7).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Quota"
                },
                "examples": {
                  "dia-corrente": {
                    "value": {
                      "tenant_id": "eduuo",
                      "period": "2026-09-21",
                      "tokens_used": 412300,
                      "tokens_cap": 2000000,
                      "tokens_remaining": 1587700,
                      "cost_micros_usd": 17317,
                      "requests_today": 640,
                      "rate_limit_per_min": 600
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "429": {
            "$ref": "#/components/responses/E429"
          }
        }
      }
    },
    "/usage": {
      "get": {
        "tags": [
          "conta"
        ],
        "operationId": "getUsage",
        "summary": "Série diária de consumo do período",
        "description": "Mesma contabilidade da quota, dia a dia, com a quebra por rota e a contagem de `pending_reconciliation` — os julgamentos cujo timeout aconteceu **depois** do envio, e cujo custo pode ter ocorrido (§7).",
        "responses": {
          "200": {
            "description": "Série diária (§4.7).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/RequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UsageReport"
                },
                "examples": {
                  "setembro": {
                    "value": {
                      "tenant_id": "eduuo",
                      "from": "2026-09-01",
                      "to": "2026-09-21",
                      "days": [
                        {
                          "date": "2026-09-21",
                          "requests": 640,
                          "input_tokens": 412300,
                          "output_tokens": 0,
                          "cost_micros_usd": 17317,
                          "routes": {
                            "auto": 380,
                            "collect_evidence": 150,
                            "human_review": 96,
                            "abstain": 14
                          },
                          "pending_reconciliation": 2
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "429": {
            "$ref": "#/components/responses/E429"
          }
        }
      }
    },
    "/openapi.json": {
      "get": {
        "tags": [
          "docs"
        ],
        "operationId": "getOpenApi",
        "summary": "Esta especificação OpenAPI 3.1",
        "description": "Fonte dos SDKs gerados e do portal de documentação.",
        "security": [],
        "responses": {
          "200": {
            "description": "O documento OpenAPI 3.1 desta borda.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Documento OpenAPI 3.1."
                }
              }
            }
          }
        }
      }
    },
    "/docs": {
      "get": {
        "tags": [
          "docs"
        ],
        "operationId": "getDocs",
        "summary": "Referência navegável (Scalar)",
        "description": "Página HTML self-contained que renderiza esta especificação.",
        "security": [],
        "responses": {
          "200": {
            "description": "Página de documentação.",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "jev_sk_*",
        "description": "Chave de API do inquilino: `jev_sk_` + 32 caracteres base64url, emitida no painel. No servidor só existe `sha256(chave)` — o texto claro aparece uma única vez. Escopos: `read` (ler), `write` (ler + julgar + rotular + publicar), `admin` (tudo, incluindo promover versão e trocar modo)."
      }
    },
    "headers": {
      "RequestId": {
        "description": "Identificador único da requisição. Presente em **toda** resposta, inclusive nas de erro; cite-o ao abrir suporte.",
        "schema": {
          "type": "string",
          "example": "req_f61b9f0ae4876c5f2df4ab9a"
        }
      },
      "RetryAfter": {
        "description": "Segundos a aguardar antes de reenviar (rate limit ou teto do inquilino). Os SDKs oficiais reesperam sozinhos.",
        "schema": {
          "type": "integer",
          "example": 60
        }
      }
    },
    "parameters": {
      "ContractId": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "Identificador do contrato no inquilino.",
        "schema": {
          "type": "string",
          "example": "ticket-router"
        }
      },
      "ContractPathName": {
        "name": "contract",
        "in": "path",
        "required": true,
        "description": "Identificador do contrato no inquilino.",
        "schema": {
          "type": "string",
          "example": "ticket-router"
        }
      },
      "ContractVersionNumber": {
        "name": "v",
        "in": "path",
        "required": true,
        "description": "Número da versão publicada (a que os recibos citam como `ticket-router@3`).",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "example": 3
        }
      },
      "ReceiptId": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "Identificador do recibo (`rcp_…`).",
        "schema": {
          "type": "string",
          "example": "rcp_01J9Z7Q2K8V4M3"
        }
      }
    },
    "responses": {
      "E400": {
        "description": "`invalid_body` — corpo malformado ou campo obrigatório ausente.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "campo obrigatório ausente: state",
              "code": "invalid_body",
              "request_id": "req_f61b9f0ae4876c5f2df4ab9a"
            }
          }
        }
      },
      "E401": {
        "description": "`unauthorized` — chave ausente, malformada ou revogada.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "chave de API ausente ou inválida",
              "code": "unauthorized",
              "request_id": "req_f61b9f0ae4876c5f2df4ab9a"
            }
          }
        }
      },
      "E403": {
        "description": "`forbidden` — escopo insuficiente (`write`/`admin` exigido).",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "escopo admin exigido para promover versão",
              "code": "forbidden",
              "request_id": "req_f61b9f0ae4876c5f2df4ab9a"
            }
          }
        }
      },
      "E404": {
        "description": "`contract_not_found` / `receipt_not_found` — id inexistente **no inquilino**.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "contrato não encontrado",
              "code": "contract_not_found",
              "request_id": "req_f61b9f0ae4876c5f2df4ab9a"
            }
          }
        }
      },
      "E409": {
        "description": "`version_conflict` — a versão já foi publicada com `fingerprint` diferente.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "versão 3 já publicada com conteúdo decisório diferente",
              "code": "version_conflict",
              "request_id": "req_f61b9f0ae4876c5f2df4ab9a"
            }
          }
        }
      },
      "E422": {
        "description": "`invalid_contract` — contrato reprovado na validação (§5).",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "choice \"fila_principal\" sem saída de escape",
              "code": "invalid_contract",
              "request_id": "req_f61b9f0ae4876c5f2df4ab9a"
            }
          }
        }
      },
      "E429": {
        "description": "`quota_exceeded` / `rate_limited` — teto do inquilino; traz `Retry-After`.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/RequestId"
          },
          "Retry-After": {
            "$ref": "#/components/headers/RetryAfter"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "teto diário de tokens do inquilino atingido",
              "code": "quota_exceeded",
              "request_id": "req_f61b9f0ae4876c5f2df4ab9a"
            }
          }
        }
      },
      "E502": {
        "description": "`provider_error` — a borda de IA falhou (§7). Nunca confundir com `abstained`: aqui o menu não foi julgado, não reprovado.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "borda de IA respondeu 503",
              "code": "provider_error",
              "request_id": "req_f61b9f0ae4876c5f2df4ab9a"
            }
          }
        }
      },
      "E504": {
        "description": "`gateway_timeout` — timeout **antes** do envio: nada foi cobrado. Timeout **depois** do envio é `200` com `status: pending_reconciliation`.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/RequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "timeout antes do envio à borda de IA",
              "code": "gateway_timeout",
              "request_id": "req_f61b9f0ae4876c5f2df4ab9a"
            }
          }
        }
      }
    },
    "schemas": {
      "ErrorEnvelope": {
        "type": "object",
        "title": "Envelope de erro",
        "description": "Corpo de **toda** resposta >= 400 (§2). O `code` é o discriminador estável para o código do consumidor; `error` é a mensagem legível e pode mudar de redação.",
        "properties": {
          "error": {
            "type": "string",
            "description": "Mensagem legível.",
            "example": "contrato não encontrado"
          },
          "code": {
            "type": "string",
            "description": "Código estável do erro.",
            "enum": [
              "invalid_body",
              "unauthorized",
              "forbidden",
              "contract_not_found",
              "receipt_not_found",
              "version_conflict",
              "invalid_contract",
              "quota_exceeded",
              "rate_limited",
              "provider_error",
              "gateway_timeout"
            ]
          },
          "request_id": {
            "type": "string",
            "description": "Mesmo valor do header `X-Request-Id`.",
            "example": "req_f61b9f0ae4876c5f2df4ab9a"
          }
        },
        "required": [
          "error",
          "code",
          "request_id"
        ]
      },
      "Error": {
        "title": "Erro",
        "description": "Envelope de erro (§2), referenciado pelas respostas padrão.",
        "allOf": [
          {
            "$ref": "#/components/schemas/ErrorEnvelope"
          }
        ]
      },
      "Mode": {
        "type": "string",
        "description": "`shadow` julga e mede sem autorizar (`enforced: false`); `enforce` autoriza ação na rota `auto`.",
        "enum": [
          "shadow",
          "enforce"
        ]
      },
      "ActionClass": {
        "type": "string",
        "description": "Classe de consequência da ação. A **barra pertence à classe**, não ao modelo: é ela que decide quanta confiança basta para agir.",
        "enum": [
          "read",
          "internal_write",
          "external_write",
          "money",
          "permission",
          "irreversible"
        ]
      },
      "Route": {
        "type": "string",
        "description": "Rota de decisão (§3.3). É a instrução para o consumidor:\n\n- `auto` — age, dentro do `allow`, só com `enforced: true`.\n- `collect_evidence` — busca **evidência nova** no estado e rejulga; **não age**.\n- `human_review` — enfileira para uma pessoa; **não age**.\n- `abstain` — a saída de escape venceu: corrija o **menu**, não a pergunta.",
        "enum": [
          "auto",
          "collect_evidence",
          "human_review",
          "abstain"
        ]
      },
      "DecisionStatus": {
        "type": "string",
        "description": "Status do julgamento (§3.3). `stale` significa que o estado mudou desde o julgamento (rejulgue); `pending_reconciliation` significa timeout **depois** do envio, e o custo pode ter ocorrido — confira o consumo.",
        "enum": [
          "decided",
          "abstained",
          "provider_error",
          "invalid_contract",
          "budget_exceeded",
          "stale",
          "pending_reconciliation"
        ]
      },
      "Instructions": {
        "description": "Instrução da pergunta, em linguagem natural. Aceita string, objeto ou array (o serviço normaliza). **O modelo não vê o id da pergunta**, então o id não instrui nada: pergunta sem `instructions` é reprovada (§5).",
        "oneOf": [
          {
            "type": "string"
          },
          {
            "type": "object",
            "additionalProperties": true
          },
          {
            "type": "array",
            "items": {}
          }
        ]
      },
      "State": {
        "description": "Estado a julgar: o material sobre o qual o julgamento é feito. Acima de `max_state_chars` (default 150000) o serviço **recusa antes de enviar** — o gateway converteria em truncamento silencioso, e truncar evidência em silêncio produz julgamento confiante sobre estado incompleto.",
        "oneOf": [
          {
            "type": "object",
            "additionalProperties": true
          },
          {
            "type": "string"
          }
        ]
      },
      "ChoiceCriteria": {
        "type": "object",
        "description": "Opções da `choice`: `{chave: descrição}`. Precisa de **saída de escape** (uma opção como `nenhum`/`indeterminado`) e de 2 a 254 opções (§5).",
        "additionalProperties": {
          "type": "string"
        },
        "minProperties": 2,
        "maxProperties": 254
      },
      "ScoreCriteria": {
        "type": "array",
        "description": "Rubrica ordenada da `score`: níveis **descritivos**, nunca ordinais (`low`, `médio`, `3` são reprovados em §5 — um nível que não se sustenta sozinho não descreve estado do mundo). Mínimo de 2 níveis.",
        "items": {
          "type": "string"
        },
        "minItems": 2
      },
      "NoulCriteria": {
        "type": "object",
        "description": "Descrição das duas respostas do `noul`. Opcional.",
        "properties": {
          "true": {
            "type": "string"
          },
          "false": {
            "type": "string"
          }
        },
        "required": [
          "true",
          "false"
        ],
        "additionalProperties": false
      },
      "ChoiceQuestion": {
        "type": "object",
        "title": "choice",
        "description": "Escolher uma opção. `criteria` é objeto `{chave: descrição}` — ou `options_from`, quando a lista de opções é viva e vem do estado.",
        "properties": {
          "type": {
            "const": "choice",
            "type": "string"
          },
          "round": {
            "type": "integer",
            "minimum": 0,
            "description": "Rodada em que a pergunta é feita (a ordem não é semântica).",
            "example": 0
          },
          "instructions": {
            "$ref": "#/components/schemas/Instructions"
          },
          "criteria": {
            "$ref": "#/components/schemas/ChoiceCriteria"
          },
          "options_from": {
            "type": "string",
            "description": "Caminho no `state` cuja lista viva (objeto `{chave: descrição}` ou array de strings/`{id,description}`) vira o critério **daquela chamada**, com a saída de escape anexada em código. Quando presente, dispensa `criteria`."
          }
        },
        "required": [
          "type",
          "instructions"
        ],
        "oneOf": [
          {
            "required": [
              "criteria"
            ]
          },
          {
            "required": [
              "options_from"
            ]
          }
        ]
      },
      "ScoreQuestion": {
        "type": "object",
        "title": "score",
        "description": "Grau numa rubrica ordenada. `criteria` é array de descrições; a resposta admite fração entre níveis.",
        "properties": {
          "type": {
            "const": "score",
            "type": "string"
          },
          "round": {
            "type": "integer",
            "minimum": 0
          },
          "instructions": {
            "$ref": "#/components/schemas/Instructions"
          },
          "criteria": {
            "$ref": "#/components/schemas/ScoreCriteria"
          }
        },
        "required": [
          "type",
          "instructions",
          "criteria"
        ]
      },
      "NoulQuestion": {
        "type": "object",
        "title": "noul",
        "description": "Probabilidade de sim/não. A resposta **não tem** `confidence`: a probabilidade **é** a incerteza entre sim e não, e `noul ~ 0.5` é ausência de sinal — não risco médio.",
        "properties": {
          "type": {
            "const": "noul",
            "type": "string"
          },
          "round": {
            "type": "integer",
            "minimum": 0
          },
          "instructions": {
            "$ref": "#/components/schemas/Instructions"
          },
          "criteria": {
            "$ref": "#/components/schemas/NoulCriteria"
          }
        },
        "required": [
          "type",
          "instructions"
        ]
      },
      "Question": {
        "title": "Pergunta",
        "description": "Primitiva de pergunta. O `type` decide quais campos de valor a resposta traz.",
        "oneOf": [
          {
            "$ref": "#/components/schemas/ChoiceQuestion"
          },
          {
            "$ref": "#/components/schemas/ScoreQuestion"
          },
          {
            "$ref": "#/components/schemas/NoulQuestion"
          }
        ],
        "discriminator": {
          "propertyName": "type",
          "mapping": {
            "choice": "#/components/schemas/ChoiceQuestion",
            "score": "#/components/schemas/ScoreQuestion",
            "noul": "#/components/schemas/NoulQuestion"
          }
        }
      },
      "ChoiceAnswer": {
        "type": "object",
        "title": "choice",
        "properties": {
          "question_id": {
            "type": "string"
          },
          "type": {
            "const": "choice",
            "type": "string"
          },
          "choice": {
            "type": "string",
            "description": "A opção vencedora."
          },
          "probabilities": {
            "type": "object",
            "additionalProperties": {
              "type": "number",
              "minimum": 0,
              "maximum": 1
            },
            "description": "Distribuição completa sobre as opções."
          },
          "confidence": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "description": "Quanta evidência sustenta a escolha."
          },
          "escape_hatch": {
            "type": "boolean",
            "description": "`true` quando a opção vencedora é a saída de escape — o menu é o problema, não a pergunta."
          }
        },
        "required": [
          "question_id",
          "type",
          "choice",
          "probabilities",
          "confidence"
        ]
      },
      "ScoreAnswer": {
        "type": "object",
        "title": "score",
        "properties": {
          "question_id": {
            "type": "string"
          },
          "type": {
            "const": "score",
            "type": "string"
          },
          "score": {
            "type": "number",
            "description": "Grau na rubrica; admite fração entre níveis."
          },
          "legend": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Rótulo de cada nível, para o recibo ser legível sem consultar o contrato."
          },
          "probabilities": {
            "type": "object",
            "additionalProperties": {
              "type": "number",
              "minimum": 0,
              "maximum": 1
            }
          },
          "confidence": {
            "type": "number",
            "minimum": 0,
            "maximum": 1
          },
          "escape_hatch": {
            "type": "boolean"
          }
        },
        "required": [
          "question_id",
          "type",
          "score",
          "probabilities",
          "confidence"
        ]
      },
      "NoulAnswer": {
        "type": "object",
        "title": "noul",
        "description": "**Não tem `confidence`** (§3.2): a probabilidade é a incerteza entre sim e não.",
        "properties": {
          "question_id": {
            "type": "string"
          },
          "type": {
            "const": "noul",
            "type": "string"
          },
          "noul": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "description": "Probabilidade de \"sim\"."
          },
          "escape_hatch": {
            "type": "boolean"
          }
        },
        "required": [
          "question_id",
          "type",
          "noul"
        ]
      },
      "Answer": {
        "title": "Resposta tipada",
        "description": "Resposta tipada de uma pergunta (§3.2). `noul` não tem `confidence`.",
        "oneOf": [
          {
            "$ref": "#/components/schemas/ChoiceAnswer"
          },
          {
            "$ref": "#/components/schemas/ScoreAnswer"
          },
          {
            "$ref": "#/components/schemas/NoulAnswer"
          }
        ],
        "discriminator": {
          "propertyName": "type",
          "mapping": {
            "choice": "#/components/schemas/ChoiceAnswer",
            "score": "#/components/schemas/ScoreAnswer",
            "noul": "#/components/schemas/NoulAnswer"
          }
        }
      },
      "StateField": {
        "type": "object",
        "description": "O estado separado por **função**, não um blob: `evidence` é o que se classifica, `fact` é contexto conhecido e `option` é o conjunto de opções vivas. A separação é o que permite recusar `state` trazendo conclusão em campo de evidência (aviso, §5).",
        "properties": {
          "path": {
            "type": "string",
            "description": "Caminho no `state`.",
            "example": "ticket.messages"
          },
          "role": {
            "type": "string",
            "enum": [
              "evidence",
              "fact",
              "option"
            ],
            "description": "Função do campo no julgamento."
          },
          "required": {
            "type": "boolean",
            "description": "Se `true`, julgar sem este campo é erro."
          },
          "note": {
            "type": "string",
            "description": "Nota para quem lê o contrato; não vai ao modelo."
          }
        },
        "required": [
          "path",
          "role"
        ]
      },
      "Condition": {
        "type": "object",
        "title": "Condição da regra",
        "description": "Condição de uma regra de rota. Todos os campos presentes são combinados com **E**. Regra sem condição é reprovada (§5) — regra sem condição não descreve estado do mundo, então ela capturaria tudo e o roteamento deixaria de ser função do julgamento.\n\nOs limiares precisam casar com a primitiva da pergunta: `score_at_least` só em `score`, `noul_at_least` só em `noul`, `confidence_*` nunca em `noul` (que não tem confiança). Misturar é reprovado em §5.",
        "properties": {
          "question": {
            "type": "string",
            "description": "Id da pergunta julgada.",
            "example": "fila_principal"
          },
          "equals": {
            "type": "string",
            "description": "A resposta da `choice` é exatamente esta opção.",
            "example": "suporte"
          },
          "score_at_least": {
            "type": "number",
            "description": "A `score` atingiu ao menos este grau (dentro da escala declarada em `criteria`)."
          },
          "noul_at_least": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "description": "O `noul` atingiu ao menos esta probabilidade."
          },
          "confidence_at_least": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "description": "A confiança da resposta atingiu ao menos este valor."
          },
          "confidence_below": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "description": "A confiança ficou abaixo deste valor."
          },
          "escape_hatch": {
            "type": "boolean",
            "description": "A resposta vencedora é (ou não é) a saída de escape."
          }
        },
        "required": [
          "question"
        ],
        "minProperties": 1
      },
      "RouteRule": {
        "type": "object",
        "description": "Regra de rota. As regras são avaliadas **na ordem**: a primeira que casar vence. `allow` é um token opaco — quem o mapeia para permissão concreta é o motor de política do consumidor; o serviço nunca executa nada.",
        "properties": {
          "when": {
            "$ref": "#/components/schemas/Condition"
          },
          "route": {
            "$ref": "#/components/schemas/Route"
          },
          "allow": {
            "type": "string",
            "description": "Token opaco de autorização liberado quando esta regra vence.",
            "example": "support:route"
          },
          "reason": {
            "type": "string",
            "description": "Razão legível, copiada para o recibo — é o que explica a decisão meses depois."
          }
        },
        "required": [
          "when",
          "route"
        ]
      },
      "ContractInput": {
        "type": "object",
        "title": "Documento de contrato (publicação)",
        "description": "Forma de arame que `POST /contracts` aceita (§3.4). `questions` é um **objeto** `{id: pergunta}` — a ordem não é semântica, o recibo ordena por id e a rodada vai em `round`.",
        "properties": {
          "id": {
            "type": "string",
            "description": "Identificador do contrato no inquilino.",
            "example": "ticket-router"
          },
          "description": {
            "type": "string",
            "description": "Descrição legível, para o painel e o histórico de versões."
          },
          "owner": {
            "type": "string",
            "description": "Responsável pelo contrato."
          },
          "mode": {
            "$ref": "#/components/schemas/Mode"
          },
          "action_class": {
            "$ref": "#/components/schemas/ActionClass"
          },
          "bar": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "description": "Confiança mínima para agir. Opcional; default = barra da classe da ação. Abaixo da barra inicial da classe gera **aviso** (§5), não erro."
          },
          "collect_floor": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "description": "Piso abaixo do qual a rota é `human_review`. Opcional; default 0.70. Acima da `bar` é reprovado."
          },
          "always_human": {
            "type": "boolean",
            "description": "Força revisão humana sempre. Opcional; default `true` apenas em `irreversible`."
          },
          "max_state_chars": {
            "type": "integer",
            "minimum": 1,
            "description": "Teto de caracteres do `state`, recusado antes do envio. Opcional; default 150000 (§7)."
          },
          "primary_question": {
            "type": "string",
            "description": "Pergunta cuja resposta decisiva governa o roteamento. Ausente ou inexistente é reprovado (§5).",
            "example": "fila_principal"
          },
          "default_route": {
            "type": "string",
            "description": "Rota quando nenhuma regra casa. **Nunca `auto`** (§5): a rota de saída não pode ser a de agir — sem isso, um contrato mal escrito autorizaria tudo por omissão.",
            "enum": [
              "collect_evidence",
              "human_review",
              "abstain"
            ]
          },
          "escalations": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Destino de cada rota que precisa de humano (ex.: `{\"human_review\": \"fila-suporte-n2\"}`)."
          },
          "state_fields": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/StateField"
            },
            "description": "O estado separado por função."
          },
          "questions": {
            "type": "object",
            "description": "Perguntas do contrato, indexadas por id.",
            "additionalProperties": {
              "$ref": "#/components/schemas/Question"
            },
            "minProperties": 1
          },
          "routes": {
            "type": "array",
            "description": "Regras avaliadas **na ordem**; a primeira que casar vence.",
            "items": {
              "$ref": "#/components/schemas/RouteRule"
            },
            "minItems": 1
          }
        },
        "required": [
          "id",
          "mode",
          "action_class",
          "primary_question",
          "default_route",
          "questions",
          "routes"
        ]
      },
      "DecisionContract": {
        "title": "Documento de contrato",
        "description": "Contrato versionado (§3.4). `version`, `fingerprint`, `created_at` e `updated_at` são **acrescentados pelo serviço**: o `fingerprint` é o sha256 do conteúdo decisório, e mudar qualquer instrução, critério, barra ou rota muda o fingerprint — é ele que o recibo cita, e é o que torna um julgamento antigo reconstruível.",
        "allOf": [
          {
            "$ref": "#/components/schemas/ContractInput"
          },
          {
            "type": "object",
            "properties": {
              "version": {
                "type": "integer",
                "minimum": 1,
                "description": "Versão publicada. Nenhuma versão é editada no lugar: mudar regra é publicar versão nova, para poder reverter."
              },
              "fingerprint": {
                "type": "string",
                "description": "sha256 do conteúdo decisório."
              },
              "created_at": {
                "type": "string",
                "format": "date-time"
              },
              "updated_at": {
                "type": "string",
                "format": "date-time"
              }
            },
            "required": [
              "version",
              "fingerprint",
              "created_at",
              "updated_at"
            ]
          }
        ]
      },
      "ThresholdReport": {
        "type": "object",
        "description": "Limiar que decidiu esta rota, com a regra que casou.",
        "properties": {
          "action_class": {
            "$ref": "#/components/schemas/ActionClass"
          },
          "bar": {
            "type": "number",
            "description": "Confiança mínima para agir nesta classe."
          },
          "collect_floor": {
            "type": "number",
            "description": "Piso da faixa do meio."
          },
          "matched_rule": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Índice (0-based) da regra que casou; `null` quando nenhuma casou e valeu o `default_route`."
          },
          "reason": {
            "type": [
              "string",
              "null"
            ],
            "description": "Razão da regra vencedora."
          }
        },
        "required": [
          "action_class",
          "bar",
          "collect_floor"
        ]
      },
      "ContractRef": {
        "type": "object",
        "description": "Referência ao contrato que julgou, congelada no recibo.",
        "properties": {
          "id": {
            "type": "string"
          },
          "version": {
            "type": "integer"
          },
          "ref": {
            "type": "string",
            "description": "`id@versão`.",
            "example": "ticket-router@3"
          },
          "fingerprint": {
            "type": "string"
          },
          "mode": {
            "$ref": "#/components/schemas/Mode"
          },
          "action_class": {
            "$ref": "#/components/schemas/ActionClass"
          }
        },
        "required": [
          "id",
          "version",
          "ref",
          "fingerprint",
          "mode",
          "action_class"
        ]
      },
      "Usage": {
        "type": "object",
        "properties": {
          "input_tokens": {
            "type": "integer",
            "minimum": 0
          },
          "output_tokens": {
            "type": "integer",
            "minimum": 0
          }
        },
        "required": [
          "input_tokens",
          "output_tokens"
        ]
      },
      "Receipt": {
        "type": "object",
        "title": "Recibo",
        "description": "Trilha auditável de um julgamento (§4.1): a distribuição inteira, o limiar aplicado, a rota, as avisos de validação e o custo. Não existe campo de execução — o serviço nunca executa nada.",
        "properties": {
          "receipt_id": {
            "type": "string",
            "example": "rcp_01J9Z7Q2K8V4M3"
          },
          "contract": {
            "$ref": "#/components/schemas/ContractRef"
          },
          "status": {
            "$ref": "#/components/schemas/DecisionStatus"
          },
          "route": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Route"
              },
              {
                "type": "null"
              }
            ],
            "description": "`null` quando não houve julgamento roteado."
          },
          "allow": {
            "type": [
              "string",
              "null"
            ],
            "description": "Token **opaco** de autorização; preenchido na rota `auto`. Quem mapeia para permissão é o motor de política do consumidor."
          },
          "enforced": {
            "type": "boolean",
            "description": "`false` em `mode: shadow` — o julgamento vem, mas o consumidor **não** está autorizado a agir."
          },
          "selected": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Answer"
              },
              {
                "type": "null"
              }
            ],
            "description": "Resposta decisiva; `null` quando o julgamento não chegou a existir (ex.: `provider_error`)."
          },
          "decisive_confidence": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "maximum": 1,
            "description": "Confiança da resposta decisiva. `null` em `noul` como pergunta primária (não há `confidence`)."
          },
          "answers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Answer"
            },
            "description": "Todas as respostas, na ordem declarada."
          },
          "threshold": {
            "$ref": "#/components/schemas/ThresholdReport"
          },
          "escalation": {
            "type": [
              "string",
              "null"
            ],
            "description": "Fila de destino quando a rota é `human_review` (valor declarado em `escalations`); `null` nas demais rotas."
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Avisos de validação (§5). Nunca são erro: o contrato é utilizável, mas vale saber."
          },
          "injected_options": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            },
            "description": "Opções vindas de `options_from` nesta chamada."
          },
          "improvement_hint": {
            "type": [
              "string",
              "null"
            ],
            "description": "Sugestão de melhoria do contrato, quando o julgamento indica que o menu está mal desenhado."
          },
          "usage": {
            "$ref": "#/components/schemas/Usage"
          },
          "cost_micros_usd": {
            "type": "integer",
            "minimum": 0,
            "description": "Custo em micro-dólares.",
            "example": 40
          },
          "latency_ms": {
            "type": "integer",
            "minimum": 0
          },
          "state_hash": {
            "type": "string",
            "description": "Hash do estado julgado.",
            "example": "sha256:9f2c41ab…"
          },
          "model_requested": {
            "type": "string",
            "example": "typesafe/jev-latest"
          },
          "model_real": {
            "type": "string",
            "description": "Modelo que de fato julgou.",
            "example": "jev-1.13.0"
          },
          "key_source": {
            "type": "string",
            "description": "De onde veio a credencial de IA (IA-01: a chave do fornecedor vive só no pool do gateway).",
            "example": "vertikon_pool"
          },
          "request_id": {
            "type": "string"
          }
        },
        "required": [
          "receipt_id",
          "contract",
          "status",
          "route",
          "enforced",
          "answers",
          "threshold",
          "usage",
          "cost_micros_usd",
          "latency_ms",
          "state_hash",
          "model_requested",
          "model_real",
          "key_source",
          "request_id"
        ]
      },
      "ReceiptList": {
        "type": "object",
        "description": "Envelope de paginação por cursor opaco (§4.7).",
        "properties": {
          "receipts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Receipt"
            }
          },
          "cursor": {
            "type": "string",
            "description": "Cursor opaco da próxima página; **ausente na última página**."
          }
        },
        "required": [
          "receipts"
        ]
      },
      "JudgeRequest": {
        "type": "object",
        "title": "Requisição de julgamento",
        "description": "Corpo do `POST /decisions/judge` (§4.1). O inquilino **nunca** é aceito no corpo: ele é derivado da chave, e `tenant_id` aqui é ignorado.",
        "properties": {
          "contract": {
            "type": "string",
            "description": "Id do contrato publicado. Usa a versão vigente.",
            "example": "ticket-router"
          },
          "contract_version": {
            "type": "integer",
            "minimum": 1,
            "description": "Fixa a versão a usar (reproduzir um julgamento antigo, testar uma versão antes de promover)."
          },
          "state": {
            "$ref": "#/components/schemas/State"
          },
          "state_version": {
            "type": "string",
            "description": "Versão do estado, para rastreio.",
            "example": "run_184:step_7"
          },
          "operation": {
            "type": "string",
            "description": "Operação de negócio que originou o julgamento.",
            "example": "support.triage"
          },
          "decision_id": {
            "type": "string",
            "description": "Id idempotente **no inquilino**: o mesmo `decision_id` não é julgado duas vezes.",
            "example": "tkt_9931"
          },
          "trace_id": {
            "type": "string",
            "description": "Propagado para os eventos NATS."
          }
        },
        "required": [
          "contract",
          "state"
        ]
      },
      "JudgeResponse": {
        "title": "Julgamento roteado",
        "description": "Corpo do `200` de `POST /decisions/judge` (§4.1) — o recibo.",
        "allOf": [
          {
            "$ref": "#/components/schemas/Receipt"
          }
        ]
      },
      "FanoutRequest": {
        "type": "object",
        "title": "Requisição de fanout",
        "description": "Corpo do `POST /decisions/fanout` (§4.2): mesmo corpo do judge, com as `questions` inline no lugar do `contract`. Ad-hoc, para perguntas soltas.",
        "properties": {
          "state": {
            "$ref": "#/components/schemas/State"
          },
          "questions": {
            "type": "object",
            "description": "Perguntas a julgar, indexadas por id.",
            "additionalProperties": {
              "$ref": "#/components/schemas/Question"
            },
            "minProperties": 1
          },
          "model": {
            "type": "string",
            "description": "Modelo da borda de IA.",
            "example": "typesafe/jev-latest"
          },
          "state_version": {
            "type": "string"
          },
          "operation": {
            "type": "string"
          },
          "decision_id": {
            "type": "string"
          },
          "trace_id": {
            "type": "string"
          }
        },
        "required": [
          "state",
          "questions"
        ]
      },
      "FanoutResponse": {
        "type": "object",
        "title": "Respostas do fanout",
        "description": "Corpo do `200` de `POST /decisions/fanout` (§4.2). Sem rotas e sem barra: devolve **só** as respostas tipadas — `status: decided` aqui **não** implica autorização de nada.",
        "properties": {
          "status": {
            "type": "string",
            "description": "Status do julgamento (§3.3). Sem rota, `decided` significa apenas que o julgamento é válido.",
            "example": "decided"
          },
          "answers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Answer"
            }
          },
          "usage": {
            "$ref": "#/components/schemas/Usage"
          },
          "latency_ms": {
            "type": "integer",
            "minimum": 0,
            "example": 900
          },
          "request_id": {
            "type": "string"
          }
        },
        "required": [
          "answers",
          "usage",
          "latency_ms",
          "request_id"
        ]
      },
      "LabelRequest": {
        "type": "object",
        "description": "Rótulo humano de um recibo (§4.4) — insumo da calibração.",
        "properties": {
          "human_label": {
            "type": "string",
            "description": "Resposta que um humano considerou correta.",
            "example": "suporte"
          },
          "labeled_by": {
            "type": "string",
            "description": "Quem rotulou.",
            "example": "ana@vertikon.com.br"
          }
        },
        "required": [
          "human_label",
          "labeled_by"
        ]
      },
      "OutcomeRequest": {
        "type": "object",
        "description": "Desfecho de produção de um julgamento em sombra (§4.4).",
        "properties": {
          "production_answer": {
            "type": "string",
            "description": "O que a produção de fato respondeu.",
            "example": "financeiro"
          },
          "action_taken": {
            "type": "boolean",
            "description": "Se a produção agiu a partir dessa resposta."
          }
        },
        "required": [
          "production_answer",
          "action_taken"
        ]
      },
      "ModeRequest": {
        "type": "object",
        "description": "Troca do modo do contrato (§4.3).",
        "properties": {
          "mode": {
            "$ref": "#/components/schemas/Mode"
          }
        },
        "required": [
          "mode"
        ]
      },
      "ConfidenceBand": {
        "type": "object",
        "title": "Faixa de confiança",
        "description": "Acerto **por faixa** de confiança. É a linha que responde \"as respostas de confiança alta são de fato mais confiáveis nesta carga?\" — se a acurácia não sobe com a confiança, a barra está no lugar errado.",
        "properties": {
          "band": {
            "type": "string",
            "description": "Intervalo da faixa.",
            "example": "0.90-1.00"
          },
          "total": {
            "type": "integer",
            "minimum": 0,
            "description": "Recibos na faixa."
          },
          "labeled": {
            "type": "integer",
            "minimum": 0,
            "description": "Recibos com rótulo humano ou resposta de produção."
          },
          "correct": {
            "type": "integer",
            "minimum": 0
          },
          "accuracy": {
            "type": "number",
            "minimum": 0,
            "maximum": 1
          }
        },
        "required": [
          "band",
          "total",
          "labeled",
          "correct",
          "accuracy"
        ]
      },
      "RouteCounts": {
        "type": "object",
        "description": "Contagem por rota decidida.",
        "properties": {
          "auto": {
            "type": "integer",
            "minimum": 0
          },
          "collect_evidence": {
            "type": "integer",
            "minimum": 0
          },
          "human_review": {
            "type": "integer",
            "minimum": 0
          },
          "abstain": {
            "type": "integer",
            "minimum": 0
          }
        },
        "additionalProperties": {
          "type": "integer",
          "minimum": 0
        }
      },
      "DivergenceReport": {
        "type": "object",
        "title": "Divergência em sombra",
        "description": "Compara o julgamento com o desfecho de produção devolvido em §4.4. É o número que autoriza (ou não) promover o contrato de sombra para enforce.",
        "properties": {
          "shadow_total": {
            "type": "integer",
            "minimum": 0
          },
          "diverged": {
            "type": "integer",
            "minimum": 0
          },
          "rate": {
            "type": "number",
            "minimum": 0,
            "maximum": 1
          }
        },
        "required": [
          "shadow_total",
          "diverged",
          "rate"
        ]
      },
      "DriftPoint": {
        "type": "object",
        "description": "Acurácia de uma versão do contrato, para comparar versões.",
        "properties": {
          "version": {
            "type": "integer",
            "minimum": 1
          },
          "accuracy": {
            "type": "number",
            "minimum": 0,
            "maximum": 1
          }
        },
        "required": [
          "version",
          "accuracy"
        ]
      },
      "CalibrationReport": {
        "type": "object",
        "title": "Calibração",
        "description": "Relatório de calibração (§4.5/§4.7): acerto por faixa de confiança, divergência em sombra, taxa de revisão humana e deriva entre versões.",
        "properties": {
          "contract": {
            "type": "string"
          },
          "version": {
            "type": "integer",
            "minimum": 1
          },
          "mode": {
            "$ref": "#/components/schemas/Mode"
          },
          "total": {
            "type": "integer",
            "minimum": 0,
            "description": "Recibos considerados."
          },
          "labeled": {
            "type": "integer",
            "minimum": 0,
            "description": "Recibos com rótulo humano ou resposta de produção."
          },
          "correct": {
            "type": "integer",
            "minimum": 0
          },
          "accuracy": {
            "type": "number",
            "minimum": 0,
            "maximum": 1
          },
          "by_band": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ConfidenceBand"
            }
          },
          "routes": {
            "$ref": "#/components/schemas/RouteCounts"
          },
          "divergence": {
            "$ref": "#/components/schemas/DivergenceReport"
          },
          "human_review_rate": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "description": "Fração do volume que ainda para em pessoa."
          },
          "drift": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DriftPoint"
            },
            "description": "Acurácia versão a versão."
          }
        },
        "required": [
          "contract",
          "version",
          "mode",
          "total",
          "labeled",
          "correct",
          "accuracy",
          "by_band",
          "routes",
          "divergence",
          "human_review_rate",
          "drift"
        ]
      },
      "EvalFailure": {
        "type": "object",
        "description": "Caso do golden set que falhou.",
        "properties": {
          "case": {
            "type": "string",
            "example": "ticket-17"
          },
          "expected": {
            "type": "string",
            "description": "Resposta do caso rotulado."
          },
          "got": {
            "type": "string",
            "description": "O que o julgamento devolveu."
          },
          "confidence": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "description": "Confiança do julgamento errado — erro com confiança alta é o que a calibração precisa ver."
          }
        },
        "required": [
          "case",
          "expected",
          "got",
          "confidence"
        ]
      },
      "EvalReport": {
        "type": "object",
        "title": "Avaliação",
        "description": "Resultado da execução do golden set (§4.5/§4.7).",
        "properties": {
          "contract": {
            "type": "string"
          },
          "version": {
            "type": "integer",
            "minimum": 1
          },
          "total": {
            "type": "integer",
            "minimum": 0
          },
          "correct": {
            "type": "integer",
            "minimum": 0
          },
          "accuracy": {
            "type": "number",
            "minimum": 0,
            "maximum": 1
          },
          "by_band": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ConfidenceBand"
            }
          },
          "failures": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EvalFailure"
            }
          }
        },
        "required": [
          "contract",
          "version",
          "total",
          "correct",
          "accuracy",
          "by_band",
          "failures"
        ]
      },
      "AccountInfo": {
        "type": "object",
        "title": "Conta",
        "description": "Identidade da chave (§4.7). O texto claro da chave nunca volta: só o prefixo.",
        "properties": {
          "tenant_id": {
            "type": "string",
            "description": "Inquilino derivado da chave."
          },
          "key_id": {
            "type": "string"
          },
          "key_prefix": {
            "type": "string",
            "example": "jev_sk_…"
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "read",
                "write",
                "admin"
              ]
            }
          },
          "plan": {
            "type": "string",
            "example": "standard"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "tenant_id",
          "key_id",
          "key_prefix",
          "scopes",
          "plan",
          "created_at"
        ]
      },
      "Quota": {
        "type": "object",
        "title": "Quota",
        "description": "Teto e consumo do período (§4.7).",
        "properties": {
          "tenant_id": {
            "type": "string"
          },
          "period": {
            "type": "string",
            "description": "Período da contabilidade (dia).",
            "example": "2026-09-21"
          },
          "tokens_used": {
            "type": "integer",
            "minimum": 0
          },
          "tokens_cap": {
            "type": "integer",
            "minimum": 0
          },
          "tokens_remaining": {
            "type": "integer",
            "minimum": 0
          },
          "cost_micros_usd": {
            "type": "integer",
            "minimum": 0
          },
          "requests_today": {
            "type": "integer",
            "minimum": 0
          },
          "rate_limit_per_min": {
            "type": "integer",
            "minimum": 0
          }
        },
        "required": [
          "tenant_id",
          "period",
          "tokens_used",
          "tokens_cap",
          "tokens_remaining",
          "cost_micros_usd",
          "requests_today",
          "rate_limit_per_min"
        ]
      },
      "UsageDay": {
        "type": "object",
        "description": "Consumo de um dia.",
        "properties": {
          "date": {
            "type": "string",
            "example": "2026-09-21"
          },
          "requests": {
            "type": "integer",
            "minimum": 0
          },
          "input_tokens": {
            "type": "integer",
            "minimum": 0
          },
          "output_tokens": {
            "type": "integer",
            "minimum": 0
          },
          "cost_micros_usd": {
            "type": "integer",
            "minimum": 0
          },
          "routes": {
            "$ref": "#/components/schemas/RouteCounts"
          },
          "pending_reconciliation": {
            "type": "integer",
            "minimum": 0,
            "description": "Julgamentos com timeout **depois** do envio: o custo pode ter ocorrido (§7)."
          }
        },
        "required": [
          "date",
          "requests",
          "input_tokens",
          "output_tokens",
          "cost_micros_usd",
          "routes",
          "pending_reconciliation"
        ]
      },
      "UsageReport": {
        "type": "object",
        "title": "Consumo",
        "description": "Série diária do período (§4.7).",
        "properties": {
          "tenant_id": {
            "type": "string"
          },
          "from": {
            "type": "string",
            "example": "2026-09-01"
          },
          "to": {
            "type": "string",
            "example": "2026-09-21"
          },
          "days": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/UsageDay"
            }
          }
        },
        "required": [
          "tenant_id",
          "from",
          "to",
          "days"
        ]
      }
    }
  }
}
