openapi: 3.0.3
info:
  title: Freight HUB API
  description: |
    Documentação das **APIs de integração** do Freight Integration HUB.

    **URL base (produção):** `https://freteiros.appcte.com/freight-hub/api`

    O HUB recebe payloads canônicos em JSON, valida com JSON Schema, roteia para o parceiro
    configurado e devolve um envelope padronizado com catálogo de rejeições `HUB-RJ-*`.

    ## Integrações ativas

    | Operação | Status |
    |----------|--------|
    | EDI CONEMB / OCOREN (Catalde) | **Ativo** |
    | CT-e averbação (AT&M) | **Ativo** — `POST /v1/cte/averbar` |
    | MDF-e averbação (AT&M) | **Ativo** — `POST /v1/mdfe/averbar` |
    | CIOT ANTT PEF (ETC, frota própria) | **Ativo** — emitir, consultar, encerrar |
    | VPO / cálculo de pedágio (Extratta) | **Desativado** |

    ### CIOT — ETC x IPEF

    - **ETC (transportadora):** não há habilitação prévia na ANTT. Com certificado ICP-Brasil (CNPJ, mTLS),
      **pode começar a enviar** em homolog ou produção.
    - **IPEF:** precisa ser **habilitada** junto à ANTT antes de operar em produção.

    **Certificado:** não há cadastro/upload no HUB. Envie `credenciais.certificado.pfxBase64` + `senha`
    em cada request (o HUB usa arquivo temporário e não persiste o PFX). Alternativa opcional: `ANTT_CERT_PATH` no servidor.
    Exemplos: `docs/exemplo-ciot-antt-*.json`.

    ## Convenções

    - Requisições e respostas em **JSON** (`Content-Type: application/json`), salvo SOAP no backend EDI.
    - Credenciais do parceiro vêm no campo `credenciais` do body (não há API key do HUB na V1).
    - Credenciais são **sanitizadas** em `hub_eventos` antes da auditoria.
    - Use `correlationId` (UUID) para replay via GET de auditoria.
  version: 1.0.0
  contact:
    name: GRDO Solutions

servers:
  - url: https://freteiros.appcte.com/freight-hub/api
    description: Produção
  - url: /
    description: Mesma origem desta página — use Try it out aqui

tags:
  - name: Sistema
    description: Health check e utilitários.
  - name: CIOT
    description: CIOT via ANTT PEF (transportadora, frota própria).
  - name: VPO
    description: Cálculo de pedágio — temporariamente indisponível.
  - name: CT-e
    description: Averbação de seguro de carga via AT&M.
  - name: MDF-e
    description: Averbação de seguro de carga via AT&M.
  - name: EDI
    description: Integrações EDI com Catalde (CONEMB e OCOREN).

paths:
  /v1/health:
    get:
      tags: [Sistema]
      summary: Health check
      operationId: healthGet
      responses:
        '200':
          description: Serviço operacional
          content:
            application/json:
              schema:
                type: object
                required: [status, timestamp]
                properties:
                  status:
                    type: string
                    example: ok
                  timestamp:
                    type: string
                    format: date-time

  /v1/cte/averbar:
    post:
      tags: [CT-e]
      summary: Averbar CT-e (AT&M)
      description: |
        Autentica em `POST /rest/Auth` (usuario, senha, codigoatm) e envia o XML do CT-e para
        `POST /rest/CTe` com `Content-Type: application/xml` e `Authorization: Bearer {token}`.

        Informe o XML em `documento.xml` (texto) ou `documento.xmlBase64`. O CT-e deve estar
        autorizado (preferencialmente `cteProc` com protocolo).

        Referência: [AT&M REST](https://webserver.averba.com.br/) — integração documentada em
        materiais da gerenciadora (Auth + CTe).
      operationId: cteAverbarPost
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CteAverbarRequest'
            example:
              destino: atm
              ambiente: prod
              credenciais:
                usuario: SEU_USUARIO_ATM
                senha: SUA_SENHA_ATM
                codigoAtm: "12345678"
              documento:
                chave: "35140512345678000199570010000012341000012345"
                xml: "<?xml version=\"1.0\" encoding=\"UTF-8\"?><cteProc>...</cteProc>"
      responses:
        '200':
          description: CT-e averbado
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CteAverbarResponse'
        '422':
          $ref: '#/components/responses/ErroValidacao'
        '502':
          $ref: '#/components/responses/ErroParceiro'

  /v1/mdfe/averbar:
    post:
      tags: [MDF-e]
      summary: Averbar MDF-e (AT&M)
      description: |
        Mesmo fluxo do CT-e: autenticação AT&M e envio do XML autorizado (`mdfeProc` ou MDFe)
        para `POST /rest/MDFe`.
      operationId: mdfeAverbarPost
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MdfeAverbarRequest'
      responses:
        '200':
          description: MDF-e averbado
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CteAverbarResponse'
        '422':
          $ref: '#/components/responses/ErroValidacao'
        '502':
          $ref: '#/components/responses/ErroParceiro'

  /v1/mdfe/averbar/{correlationId}:
    get:
      tags: [MDF-e]
      summary: Auditoria de averbação MDF-e
      operationId: mdfeAverbarGet
      parameters:
        - $ref: '#/components/parameters/CorrelationId'
      responses:
        '200':
          description: Evento encontrado
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EventoAuditoria'
        '404':
          description: Não encontrado
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErroSimples'

  /v1/cte/averbar/{correlationId}:
    get:
      tags: [CT-e]
      summary: Auditoria de averbação CT-e
      operationId: cteAverbarGet
      parameters:
        - $ref: '#/components/parameters/CorrelationId'
      responses:
        '200':
          description: Evento encontrado
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EventoAuditoria'
        '404':
          description: Não encontrado
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErroSimples'

  /v1/ciot:
    post:
      tags: [CIOT]
      summary: Emitir CIOT (ANTT PEF)
      description: |
        Declara operação de transporte (`DeclaracaoOperacaoTransporte`).

        **ETC:** basta certificado ICP-Brasil da transportadora — **sem cadastro prévio na ANTT** (diferente de IPEF).

        **Frota própria:** `proprietario.cnpjCpf` = `credenciais.cnpjTransportador`;
        `veiculo.tipoContrato` e `proprietario.tipoContrato` = `FROTA`.

        Schema: `services/Schema/definitions/ciot/emitir.v1.json`
      operationId: ciotPost
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CiotAnttEmitirRequest'
            example:
              destino: antt
              ambiente: homolog
              credenciais:
                cnpjTransportador: "11222333000181"
                certificado:
                  pfxBase64: "<base64 do .pfx>"
                  senha: "***"
              frete:
                idOperacaoTransporte: "560000134130"
                tipoOperacao: LOTACAO
                valorFrete: 5000.0
                documentos:
                  clienteOrigemCpfCnpj: "12345678000199"
                  clienteDestinoCpfCnpj: "98765432000188"
                  motoristaCpf: "12345678901"
                  proprietarioCpfCnpj: "11222333000181"
                proprietario:
                  cnpjCpf: "11222333000181"
                  razaoSocial: TRANSPORTES EXEMPLO LTDA
                  rntrc: "12345678"
                  tipoContrato: FROTA
                veiculo:
                  placa: ABC1D23
                  chassi: 9BWZZZ377VT004251
                  renavam: "12345678901"
                  marca: VOLVO
                  modelo: FH 540
                  comTracao: true
                  tipoContrato: FROTA
                  quantidadeEixos: 6
                viagem:
                  distanciaKm: 850.5
                  dataColeta: "2026-06-15"
                  dataPrevisaoEntrega: "2026-06-16"
                  codigoTipoCarga: 5
                  origem: { municipio: Sao Paulo, uf: SP, cep: "01001000" }
                  destino: { municipio: Curitiba, uf: PR, cep: "80010000" }
                valores:
                  pesoSaida: 30000
                  quantidade: 1
                pagamento:
                  tipo: PIX
                  cpfCnpjCreditado: "11222333000181"
                  indPagamento: AVISTA
                  pix:
                    chavePix: "11222333000181"
                    identificadorPix: PIX-REF-001
                  parcelas:
                    - numero: 1
                      valor: 5000.0
      responses:
        '200':
          description: CIOT declarado
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CiotAnttOperacaoResponse'
              example:
                correlationId: 11111111-1111-4111-8111-111111111111
                status: sucesso
                destino: antt
                ciot:
                  numero: "560000134130"
                  verificador: "1234"
                  completo: "5600001341301234"
                  protocolo: PROTO-ANTT-001
                rejeicoes: []
                duracaoMs: 980
        '422':
          $ref: '#/components/responses/ErroValidacao'
        '502':
          $ref: '#/components/responses/ErroParceiro'

  /v1/ciot/consultar:
    post:
      tags: [CIOT]
      summary: Consultar CIOT na ANTT
      description: |
        Serviço `ConsultarCIOTGerado`. Informe o CIOT **sem** dígito verificador (12 dígitos).

        Mesmas `credenciais` da emissão (certificado mTLS).
      operationId: ciotConsultarPost
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CiotAnttConsultarRequest'
            example:
              destino: antt
              ambiente: homolog
              credenciais:
                cnpjTransportador: "11222333000181"
                certificado:
                  pfxBase64: "<base64 do .pfx>"
                  senha: "***"
              ciot:
                codigoIdentificacao: "560000134130"
                anoDeclaracao: 2026
      responses:
        '200':
          description: Consulta realizada
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CiotAnttOperacaoResponse'
        '422':
          $ref: '#/components/responses/ErroValidacao'
        '502':
          $ref: '#/components/responses/ErroParceiro'

  /v1/ciot/encerrar:
    post:
      tags: [CIOT]
      summary: Encerrar operação de transporte
      description: |
        Serviço `EncerramentoOperacaoTransporte`.

        Identifique o CIOT com `ciot.numero` + `ciot.verificador` ou `ciot.codigoIdentificacaoCompleto` (16 dígitos).
        Para `TAC_AGREGADO`, informe `encerramento.origemDestino` (ou `frete.viagem` no body).
      operationId: ciotEncerrarPost
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CiotAnttEncerrarRequest'
            example:
              destino: antt
              ambiente: homolog
              credenciais:
                cnpjTransportador: "11222333000181"
                certificado:
                  pfxBase64: "<base64 do .pfx>"
                  senha: "***"
              ciot:
                numero: "560000134130"
                verificador: "1234"
              encerramento:
                pesoCarga: 29500
                origemDestino:
                  origem: { municipio: Sao Paulo, uf: SP }
                  destino: { municipio: Curitiba, uf: PR }
                  distanciaKm: 850
                  qtdViagens: 1
      responses:
        '200':
          description: Encerramento aceito
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CiotAnttOperacaoResponse'
        '422':
          $ref: '#/components/responses/ErroValidacao'
        '502':
          $ref: '#/components/responses/ErroParceiro'

  /v1/ciot/{correlationId}:
    get:
      tags: [CIOT]
      summary: Auditoria / replay de evento CIOT
      description: |
        Retorna o payload canônico (credenciais sanitizadas), o enviado ao parceiro e a resposta bruta
        gravados em `hub_eventos`. Funciona para eventos históricos mesmo com emissão desativada.
      operationId: ciotGet
      parameters:
        - $ref: '#/components/parameters/CorrelationId'
      responses:
        '200':
          description: Evento encontrado
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EventoAuditoria'
        '404':
          description: correlationId não encontrado
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErroSimples'

  /v1/vpo/calcular:
    post:
      tags: [VPO]
      summary: Calcular custo de pedágio (indisponível)
      description: |
        **Temporariamente indisponível (HTTP 503).**

        Alias: `POST /v1/pedagio/calcular`. Dependia da integração Extratta.
      operationId: vpoCalcularPost
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
      responses:
        '503':
          description: Integração desativada
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Indisponivel'

  /v1/vpo/{correlationId}:
    get:
      tags: [VPO]
      summary: Auditoria de consulta de pedágio
      operationId: vpoGet
      parameters:
        - $ref: '#/components/parameters/CorrelationId'
      responses:
        '200':
          description: Evento encontrado
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EventoAuditoria'
        '404':
          description: Não encontrado
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErroSimples'

  /v1/edi/conemb:
    post:
      tags: [EDI]
      summary: Enviar lote CONEMB (Catalde)
      description: |
        Valida o payload canônico `edi/conemb.v1` e traduz para SOAP `receberConhecimentosV4`.

        **destino:** `catalde` (default se omitido).
      operationId: ediConembPost
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ConembRequest'
      responses:
        '200':
          description: Lote processado (pode conter documentos rejeitados no corpo)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConembResponse'
        '422':
          $ref: '#/components/responses/ErroValidacao'
        '502':
          $ref: '#/components/responses/ErroParceiro'

  /v1/edi/conemb/{correlationId}:
    get:
      tags: [EDI]
      summary: Auditoria CONEMB
      operationId: ediConembGet
      parameters:
        - $ref: '#/components/parameters/CorrelationId'
      responses:
        '200':
          description: Evento encontrado
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EventoAuditoria'
        '404':
          description: Não encontrado
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErroSimples'

  /v1/edi/ocorren:
    post:
      tags: [EDI]
      summary: Enviar ocorrências OCOREN (Catalde)
      description: |
        Alias: `POST /v1/edi/ocoren`. Schema `edi/ocorren.v1`, SOAP `receberRastreamento`.
      operationId: ediOcorrenPost
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/OcorrenRequest'
      responses:
        '200':
          description: Ocorrências aceitas
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OcorrenResponse'
        '422':
          $ref: '#/components/responses/ErroValidacao'
        '502':
          $ref: '#/components/responses/ErroParceiro'

  /v1/edi/ocorren/{correlationId}:
    get:
      tags: [EDI]
      summary: Auditoria OCOREN
      operationId: ediOcorrenGet
      parameters:
        - $ref: '#/components/parameters/CorrelationId'
      responses:
        '200':
          description: Evento encontrado
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EventoAuditoria'
        '404':
          description: Não encontrado
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErroSimples'

components:
  parameters:
    CorrelationId:
      name: correlationId
      in: path
      required: true
      schema:
        type: string
        format: uuid
      description: UUID do evento retornado na emissão ou gerado pelo cliente.

  responses:
    ErroValidacao:
      description: Payload inválido (JSON Schema)
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErroValidacao'
    ErroParceiro:
      description: Falha de comunicação ou erro HTTP do parceiro
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/EnvelopeOperacao'

  schemas:
    Indisponivel:
      type: object
      required: [status, correlationId, mensagem]
      properties:
        status:
          type: string
          enum: [indisponivel]
        correlationId:
          type: string
          format: uuid
        mensagem:
          type: string

    CteAverbarRequest:
      type: object
      required: [destino, credenciais, documento]
      properties:
        correlationId:
          type: string
          format: uuid
        destino:
          type: string
          enum: [atm]
        ambiente:
          type: string
          enum: [homolog, prod]
        credenciais:
          type: object
          required: [usuario, senha, codigoAtm]
          properties:
            usuario:
              type: string
            senha:
              type: string
            codigoAtm:
              type: string
        documento:
          type: object
          properties:
            chave:
              type: string
              pattern: '^[0-9]{44}$'
            xml:
              type: string
            xmlBase64:
              type: string

    MdfeAverbarRequest:
      type: object
      required: [destino, credenciais, documento]
      properties:
        correlationId:
          type: string
          format: uuid
        destino:
          type: string
          enum: [atm]
        ambiente:
          type: string
          enum: [homolog, prod]
        credenciais:
          type: object
          required: [usuario, senha, codigoAtm]
          properties:
            usuario:
              type: string
            senha:
              type: string
            codigoAtm:
              type: string
        documento:
          type: object
          properties:
            chave:
              type: string
              pattern: '^[0-9]{44}$'
            xml:
              type: string
            xmlBase64:
              type: string

    CteAverbarResponse:
      allOf:
        - $ref: '#/components/schemas/EnvelopeOperacao'
        - type: object
          properties:
            averbacao:
              type: object
              properties:
                chave:
                  type: string
                protocolo:
                  type: string
                dataAverbacao:
                  type: string
                numeroAverbacao:
                  type: string
                apolice:
                  type: string

    AnttCertificado:
      type: object
      required: [senha]
      properties:
        senha:
          type: string
          description: Senha do arquivo PFX
        pfxBase64:
          type: string
          description: |
            Conteúdo do arquivo .pfx em base64. **Modo recomendado** — sem cadastro de certificado no HUB.
            Renovação = enviar novo base64 na próxima chamada.
        pfxPath:
          type: string
          description: Caminho do PFX no disco do servidor HUB (opcional; integração avançada)

    AnttCredenciais:
      type: object
      required: [cnpjTransportador, certificado]
      properties:
        cnpjTransportador:
          type: string
          pattern: '^[0-9]{14}$'
          description: CNPJ da ETC (titular do certificado e contratado)
        certificado:
          $ref: '#/components/schemas/AnttCertificado'

    CiotAnttPontoGeo:
      type: object
      properties:
        latitude: { type: number }
        longitude: { type: number }
        municipio: { type: string }
        uf: { type: string, pattern: '^[A-Z]{2}$' }
        ibgeCidade: { type: integer }
        cep: { type: string, pattern: '^[0-9]{8}$' }

    CiotAnttPagamento:
      type: object
      required: [tipo, cpfCnpjCreditado, indPagamento]
      properties:
        tipo:
          type: string
          enum: [IP, CONTA_CORRENTE, CONTA_POUPANCA, CONTA_PAGAMENTO, OUTROS, PIX]
        cpfCnpjCreditado:
          type: string
        indPagamento:
          type: string
          enum: [AVISTA, APRAZO]
        codigoPagamento: { type: string }
        banco:
          type: object
          properties:
            codigo: { type: string }
            agencia: { type: string }
            conta: { type: string }
        pix:
          type: object
          properties:
            chavePix: { type: string }
            identificadorPix: { type: string }
        parcelas:
          type: array
          items:
            type: object
            required: [valor]
            properties:
              numero: { type: integer }
              valor: { type: number }
              dataVencimento: { type: string, format: date }

    CiotAnttFreteEmitir:
      type: object
      required: [tipoOperacao, documentos, valores, veiculo, viagem, proprietario, pagamento]
      properties:
        idOperacaoTransporte:
          type: string
          pattern: '^[0-9]{12}$'
          description: 12 dígitos; opcional se o servidor gera via ANTT_ID_GERADOR_CMD
        tipoOperacao:
          type: string
          enum: [LOTACAO, FRACIONADA, TAC_AGREGADO]
        contratantesCargaFrac:
          type: array
          description: Demais tomadores em FRACIONADA (regra B119 — sem repetir clienteTomadorCpfCnpj).
          items:
            type: string
        valorFrete: { type: number }
        naturezaCarga: { type: integer }
        dataDeclaracao: { type: string }
        documentos:
          type: object
          required: [motoristaCpf, proprietarioCpfCnpj]
          properties:
            clienteOrigemCpfCnpj:
              type: string
              description: Remetente / origem (nao e o contratante principal se clienteTomadorCpfCnpj estiver preenchido).
            clienteDestinoCpfCnpj:
              type: string
              description: Destinatario da carga.
            clienteTomadorCpfCnpj:
              type: string
              description: Tomador do frete = CpfCnpjContratante na ANTT (contratante principal).
            motoristaCpf: { type: string }
            proprietarioCpfCnpj: { type: string }
        proprietario:
          type: object
          required: [cnpjCpf, razaoSocial, rntrc, tipoContrato]
          properties:
            cnpjCpf: { type: string }
            razaoSocial: { type: string }
            rntrc: { type: string }
            tipoContrato:
              type: string
              enum: [FROTA]
        motorista: { type: object, description: Ver emitir.v1.json ($defs/motorista) }
        veiculo:
          type: object
          required: [placa, quantidadeEixos, tipoContrato]
          properties:
            placa: { type: string }
            quantidadeEixos: { type: integer }
            tipoContrato:
              type: string
              enum: [FROTA]
            chassi: { type: string }
            renavam: { type: string }
            marca: { type: string }
            modelo: { type: string }
            comTracao: { type: boolean }
        carretas:
          type: array
          items:
            type: object
            properties:
              placa: { type: string }
              quantidadeEixos: { type: integer }
              tipoContrato: { type: string, enum: [FROTA] }
        viagem:
          type: object
          required: [distanciaKm, origem, destino]
          properties:
            distanciaKm: { type: number }
            dataColeta: { type: string }
            dataPrevisaoEntrega: { type: string }
            codigoTipoCarga: { type: integer, minimum: 1, maximum: 12 }
            altoDesempenho: { type: boolean }
            retornoVazio: { type: boolean }
            composicaoVeicular: { type: boolean }
            origem: { $ref: '#/components/schemas/CiotAnttPontoGeo' }
            destino: { $ref: '#/components/schemas/CiotAnttPontoGeo' }
        valores:
          type: object
          required: [pesoSaida, quantidade]
          properties:
            pesoSaida: { type: number }
            quantidade: { type: number }
            valorMercadoria: { type: number }
        pagamento:
          $ref: '#/components/schemas/CiotAnttPagamento'

    CiotAnttEmitirRequest:
      type: object
      required: [destino, credenciais, frete]
      properties:
        correlationId: { type: string, format: uuid }
        destino: { type: string, enum: [antt] }
        ambiente: { type: string, enum: [homolog, prod] }
        credenciais: { $ref: '#/components/schemas/AnttCredenciais' }
        frete: { $ref: '#/components/schemas/CiotAnttFreteEmitir' }

    CiotAnttConsultarRequest:
      type: object
      required: [destino, credenciais, ciot]
      properties:
        correlationId: { type: string, format: uuid }
        destino: { type: string, enum: [antt] }
        ambiente: { type: string, enum: [homolog, prod] }
        credenciais: { $ref: '#/components/schemas/AnttCredenciais' }
        ciot:
          type: object
          required: [codigoIdentificacao]
          properties:
            codigoIdentificacao:
              type: string
              pattern: '^[0-9]{12}$'
            anoDeclaracao: { type: integer }

    CiotAnttEncerrarRequest:
      type: object
      required: [destino, credenciais, ciot]
      properties:
        correlationId: { type: string, format: uuid }
        destino: { type: string, enum: [antt] }
        ambiente: { type: string, enum: [homolog, prod] }
        credenciais: { $ref: '#/components/schemas/AnttCredenciais' }
        ciot:
          type: object
          properties:
            numero: { type: string, pattern: '^[0-9]{12}$' }
            verificador: { type: string, pattern: '^[0-9]{4}$' }
            codigoIdentificacaoCompleto: { type: string, minLength: 16, maxLength: 16 }
        encerramento:
          type: object
          properties:
            pesoCarga: { type: number }
            qtdViagens: { type: integer }
            origemDestino:
              type: object
              properties:
                origem: { $ref: '#/components/schemas/CiotAnttPontoGeo' }
                destino: { $ref: '#/components/schemas/CiotAnttPontoGeo' }
                distanciaKm: { type: number }
                qtdViagens: { type: integer }
        frete:
          type: object
          properties:
            viagem:
              type: object
              properties:
                origem: { $ref: '#/components/schemas/CiotAnttPontoGeo' }
                destino: { $ref: '#/components/schemas/CiotAnttPontoGeo' }
                distanciaKm: { type: number }

    CiotAnttResultado:
      type: object
      properties:
        numero: { type: string, description: CIOT 12 dígitos }
        verificador: { type: string, description: DV 4 dígitos }
        completo: { type: string, description: CIOT + DV }
        protocolo: { type: string }
        avisoTransportador: { type: string }

    CiotAnttOperacaoResponse:
      allOf:
        - $ref: '#/components/schemas/EnvelopeOperacao'
        - type: object
          properties:
            ciot:
              $ref: '#/components/schemas/CiotAnttResultado'
            protocolo:
              type: string
              description: Alias do CIOT completo ou protocolo ANTT

    ErroSimples:
      type: object
      properties:
        status:
          type: string
        mensagem:
          type: string

    ErroValidacao:
      type: object
      required: [status, correlationId, mensagem, erros]
      properties:
        status:
          type: string
          enum: [erro_validacao]
        correlationId:
          type: string
          format: uuid
        mensagem:
          type: string
        erros:
          type: array
          items:
            type: object
            properties:
              campo:
                type: string
              mensagem:
                type: string

    Rejeicao:
      type: object
      properties:
        codigoHub:
          type: string
          example: HUB-RJ-001
        codigoParceiro:
          type: string
          nullable: true
        mensagem:
          type: string
        severidade:
          type: string
          enum: [erro, aviso]

    EnvelopeOperacao:
      type: object
      properties:
        correlationId:
          type: string
          format: uuid
        status:
          type: string
          enum: [sucesso, rejeitado, erro_parceiro, erro_interno, indisponivel]
        destino:
          type: string
        ambiente:
          type: string
          enum: [homolog, prod]
        eventoId:
          type: integer
          nullable: true
        rejeicoes:
          type: array
          items:
            $ref: '#/components/schemas/Rejeicao'
        mensagemParceiro:
          type: string
          nullable: true
        duracaoMs:
          type: integer

    EventoAuditoria:
      type: object
      description: Replay de hub_eventos (credenciais sanitizadas).
      properties:
        correlationId:
          type: string
          format: uuid
        tipoEvento:
          type: string
        destino:
          type: string
        ambiente:
          type: string
        canonical:
          type: object
        payloadEnviado:
          type: string
          nullable: true
        respostaParceiro:
          type: string
          nullable: true
        status:
          type: string
        criadoEm:
          type: string

    CredenciaisCatalde:
      type: object
      required: [cnpjTransportadora, razaoSocial, chaveAcesso]
      properties:
        cnpjTransportadora:
          type: string
          pattern: '^[0-9]{14}$'
        razaoSocial:
          type: string
        chaveAcesso:
          type: string
        audit:
          type: object
          properties:
            nome:
              type: string
            documento:
              type: string

    ConembRequest:
      type: object
      required: [destino, credenciais, lote]
      properties:
        correlationId:
          type: string
          format: uuid
        destino:
          type: string
          default: catalde
          example: catalde
        ambiente:
          type: string
          enum: [homolog, prod]
          default: prod
        credenciais:
          $ref: '#/components/schemas/CredenciaisCatalde'
        lote:
          type: object
          required: [documentos]
          properties:
            identificadorLote:
              type: string
            documentos:
              type: array
              minItems: 1
              items:
                type: object
                description: Ver schema completo edi/conemb.v1 no repositório.

    ConembResponse:
      allOf:
        - $ref: '#/components/schemas/EnvelopeOperacao'
        - type: object
          properties:
            lote:
              type: object
              properties:
                importados:
                  type: integer
                rejeitados:
                  type: integer
            documentos:
              type: array
              items:
                type: object

    OcorrenRequest:
      type: object
      required: [destino, credenciais, lote]
      properties:
        correlationId:
          type: string
          format: uuid
        destino:
          type: string
          default: catalde
        ambiente:
          type: string
          enum: [homolog, prod]
        credenciais:
          $ref: '#/components/schemas/CredenciaisCatalde'
        lote:
          type: object
          required: [ocorrencias]
          properties:
            identificadorLote:
              type: string
            ocorrencias:
              type: array
              minItems: 1
              items:
                type: object

    OcorrenResponse:
      allOf:
        - $ref: '#/components/schemas/EnvelopeOperacao'
        - type: object
          properties:
            protocolo:
              type: string
              nullable: true
