openapi: 3.0.3
info:
  title: AppCTE API
  description: |
    Documentação das **APIs públicas de integração** do AppCTE.

    **URL base:** `https://appcte.com.br`

    Cada operação abaixo descreve autenticação, parâmetros e formatos do respectivo recurso.
    Novos endpoints serão incluídos nesta mesma especificação conforme forem disponibilizados.

    ## Convenções

    - Requisições e respostas em **JSON** (`Content-Type: application/json`), salvo indicação no endpoint.
    - Toda resposta inclui `request_id` — informe ao suporte em caso de dúvida.
    - Em operações em **lote**, HTTP `200` não garante sucesso de todos os itens; verifique o corpo da resposta.
  version: 2.1.3
  contact:
    name: AppCTE

servers:
  - url: https://appcte.com.br
    description: Produção (API e documentação)
  - url: /
    description: Mesma origem desta página — use Try it out aqui

tags:
  - name: CT-e
    description: Emissão de Conhecimento de Transporte Eletrônico.

paths:
  /api/cte_embarcador:
    post:
      tags: [CT-e]
      summary: Enviar lote de CT-e (integração embarcador)
      description: |
        Registra um ou mais CT-e para a transportadora emitente e, por padrão, solicita autorização na SEFAZ.

        **Content-Type:** `application/json`

        **Corpo:** array JSON na raiz — `[{ ... }, { ... }]`. Não envie `{ "itens": [...] }`.

        ### Autenticação (este endpoint)

        1. Token de integração: `Authorization: Bearer <token>` (ou header `X-Embarcador-Token`).
        2. CNPJ do emitente: header `X-Emitente-CNPJ` (14 caracteres, numérico ou alfanumérico). O token deve estar ativo para esse emitente.

        ### Transmissão e `chave`

        Padrão: grava e transmite na mesma requisição. Apenas gravar: `?transmit=0`.

        | `fase` | `chave` / `protocolo` |
        |--------|------------------------|
        | `autorizado` | Sim (44 dígitos + protocolo) |
        | `gravado_sem_transmissao` | Não — use `numero`, `serie`, `cte_id` |
        | `transmissao_rejeitada` | Não — veja `mensagem` e `detalhes` |

        ### Payload (por item do array)

        **Obrigatórios:** `Remetente`, `Destinatario`, `Tomador` (alias `Pagador`), `ValorTotalFrete` (> 0),
        `DadosNotaFiscal` (ao menos uma nota com `KeyNotaFiscal` de 44 dígitos).

        **Atores:** em cada parceiro, `CNPJCPF`, `RazaoSocial`, `Rua`, `Numero`, `Bairro`, `Cep` e
        `CidadeIBGE` ou `Cidade` + `UnidadeFederacao`. `Tomador` = pagador do frete (CNPJ igual ao remetente → tipo 0;
        ao destinatário → tipo 3; outro → tipo 4).

        **Opcionais:** `PesoTotal`, `NumeroProtocolo`, `ValorComposicaoFrete`, `DadosCIOT`, `DadosVPO`, `Observacoes`.

        O frete no CT-e é o valor de `ValorTotalFrete`. CFOP é calculado automaticamente (`cfop` na resposta).
      operationId: cteEmbarcadorPost
      security:
        - bearerAuth: []
        - embarcadorToken: []
      parameters:
        - $ref: '#/components/parameters/EmitenteCnpj'
        - name: transmit
          in: query
          description: |
            `1` ou omitido — grava e transmite (padrão).
            `0` — apenas grava (`fase: gravado_sem_transmissao`).
          schema:
            type: string
            enum: ['0', '1']
            default: '1'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CteEmbarcadorRequest'
            examples:
              padrao:
                summary: Layout padrão — CT-e completo com CIOT e VPO
                value:
                  - Remetente:
                      CNPJCPF: "11111111000191"
                      RazaoSocial: EMPRESA FICTICIA REMETENTE LTDA
                      InscricaoEstadual: ISENTO
                      Rua: RUA FICTICIA UM
                      Numero: "100"
                      Complemento: SALA 1
                      Bairro: BAIRRO FICTICIO
                      Cidade: CIDADE FICTICIA A
                      CidadeIBGE: "3550308"
                      Cep: "01001000"
                      UnidadeFederacao: SP
                      TelefoneContato: "11999990001"
                      TipoIdentificacao: CNPJ
                    Destinatario:
                      CNPJCPF: "22222222000192"
                      RazaoSocial: EMPRESA FICTICIA DESTINATARIA LTDA
                      InscricaoEstadual: ISENTO
                      Rua: RUA FICTICIA DOIS
                      Numero: "200"
                      Bairro: BAIRRO FICTICIO
                      Cidade: CIDADE FICTICIA B
                      CidadeIBGE: "4106902"
                      Cep: "80010000"
                      UnidadeFederacao: PR
                    Tomador:
                      CNPJCPF: "11111111000191"
                      RazaoSocial: EMPRESA FICTICIA REMETENTE LTDA
                      InscricaoEstadual: ISENTO
                      Rua: RUA FICTICIA UM
                      Numero: "100"
                      Bairro: BAIRRO FICTICIO
                      Cidade: CIDADE FICTICIA A
                      CidadeIBGE: "3550308"
                      Cep: "01001000"
                      UnidadeFederacao: SP
                    ValorTotalFrete: 85.50
                    ValorComposicaoFrete:
                      - CodigoServico: 1
                        DescricaoServico: FRETE PESO
                        ValorServico: 45.00
                      - CodigoServico: 8
                        DescricaoServico: GRIS
                        ValorServico: 25.50
                      - CodigoServico: 11
                        DescricaoServico: PEDAGIO
                        ValorServico: 15.00
                    PesoTotal: 12000
                    PesoDensidadeCubagemTotal: 0.8
                    NumeroProtocolo: INT-FICTICIO-0001
                    DadosCIOT:
                      Numero: "123456789012"
                      CNPJ: "11111111000191"
                      CNPJIPEF: "44444444000194"
                    DadosVPO:
                      NumeroCompra: "000000000001"
                      Valor: 15.00
                      CNPJFornecedor: "55555555000195"
                      tpValePed: "01"
                    DadosNotaFiscal:
                      - CNPJCPFEmissorNotaFiscal: "11111111000191"
                        NumeroNotaFiscal: "000000123"
                        SerieNotaFiscal: "1"
                        KeyNotaFiscal: "35260111111111000191550010000000011234567890"
                        MeioTransporte: RODOVIARIO
                        TipoTransporteCarga: CARGA FRACIONADA
                        ValorTotalNota: 5890.75
                        NumeroPedido: PED-FICTICIO-001
                        DataEmissao: "2026-01-15T00:00:00"
                        NaturezaMercadoria: MERCADORIA FICTICIA
                        NCM: "00000000"
                        EspecieAcondicionamento: CAIXAS
                        QuantidadeVolumes: 15
                        PesoTotalNota: 8000
                        PesoDensidadeCubagem: 0.5
                        NumeroProtocoloNotaFiscal: 0
              lote:
                summary: Dois CT-e no mesmo POST
                value:
                  - ValorTotalFrete: 800.0
                    Remetente:
                      CNPJCPF: "11111111000191"
                      RazaoSocial: EMPRESA FICTICIA REMETENTE LTDA
                      Rua: RUA FICTICIA UM
                      Numero: "100"
                      Bairro: BAIRRO FICTICIO
                      Cep: "01001000"
                      CidadeIBGE: "3550308"
                      UnidadeFederacao: SP
                    Destinatario:
                      CNPJCPF: "22222222000192"
                      RazaoSocial: EMPRESA FICTICIA DESTINATARIA LTDA
                      Rua: RUA FICTICIA DOIS
                      Numero: "200"
                      Bairro: BAIRRO FICTICIO
                      Cep: "80010000"
                      CidadeIBGE: "4106902"
                      UnidadeFederacao: PR
                    Tomador:
                      CNPJCPF: "11111111000191"
                      RazaoSocial: EMPRESA FICTICIA REMETENTE LTDA
                      Rua: RUA FICTICIA UM
                      Numero: "100"
                      Bairro: BAIRRO FICTICIO
                      Cep: "01001000"
                      CidadeIBGE: "3550308"
                      UnidadeFederacao: SP
                    DadosNotaFiscal:
                      - KeyNotaFiscal: "35260111111111000191550010000000011234567890"
                  - ValorTotalFrete: 900.0
                    Remetente:
                      CNPJCPF: "11111111000191"
                      RazaoSocial: EMPRESA FICTICIA REMETENTE LTDA
                      Rua: RUA FICTICIA UM
                      Numero: "100"
                      Bairro: BAIRRO FICTICIO
                      Cep: "01001000"
                      CidadeIBGE: "3550308"
                      UnidadeFederacao: SP
                    Destinatario:
                      CNPJCPF: "22222222000192"
                      RazaoSocial: EMPRESA FICTICIA DESTINATARIA LTDA
                      Rua: RUA FICTICIA DOIS
                      Numero: "200"
                      Bairro: BAIRRO FICTICIO
                      Cep: "80010000"
                      CidadeIBGE: "4106902"
                      UnidadeFederacao: PR
                    Tomador:
                      CNPJCPF: "22222222000192"
                      RazaoSocial: EMPRESA FICTICIA DESTINATARIA LTDA
                      Rua: RUA FICTICIA DOIS
                      Numero: "200"
                      Bairro: BAIRRO FICTICIO
                      Cep: "80010000"
                      CidadeIBGE: "4106902"
                      UnidadeFederacao: PR
                    DadosNotaFiscal:
                      - KeyNotaFiscal: "35260122222222000192550010000000021234567891"
      responses:
        '200':
          description: |
            Processamento concluído. HTTP 200 mesmo com falha parcial — verifique `sucesso` e cada item em `itens`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CteEmbarcadorResponse'
              examples:
                todosOk:
                  summary: Todos autorizados
                  value:
                    sucesso: true
                    mensagem: Todos os itens foram processados com sucesso.
                    request_id: a1b2c3d4e5f6789012345678abcdef01
                    itens:
                      - indice: 0
                        sucesso: true
                        fase: autorizado
                        cte_id: 42
                        numero: 1001
                        serie: 1
                        cfop: "5352"
                        chave: "35260199999999000199570010000010011234567890"
                        protocolo: "199999999999999"
                        mensagem: Autorizado com sucesso.
                parcial:
                  summary: Um item falhou
                  value:
                    sucesso: false
                    mensagem: Um ou mais itens falharam; veja o array itens.
                    request_id: b2c3d4e5f6789012345678abcdef012345
                    itens:
                      - indice: 0
                        sucesso: true
                        fase: autorizado
                        cte_id: 43
                        numero: 1002
                        serie: 1
                        chave: "35260199999999000199570010000010021234567891"
                      - indice: 1
                        sucesso: false
                        fase: transmissao_rejeitada
                        erro_codigo: SEFAZ_OR_API
                        cte_id: 44
                        numero: 1003
                        serie: 1
                        mensagem: Rejeição na autorização
                        detalhes:
                          cStat: "XXX"
                          xMotivo: Motivo informado pela SEFAZ
                gravadoSemTx:
                  summary: transmit=0
                  value:
                    sucesso: true
                    mensagem: Todos os itens foram processados com sucesso.
                    request_id: c3d4e5f6789012345678abcdef01234567
                    itens:
                      - indice: 0
                        sucesso: true
                        fase: gravado_sem_transmissao
                        cte_id: 45
                        numero: 1004
                        serie: 1
                        cfop: "5932"
                        mensagem: CT-e registrado sem envio à SEFAZ.
        '400':
          description: Requisição inválida (CNPJ emitente, corpo vazio, JSON inválido)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiErrorResponse'
              examples:
                cnpj:
                  value:
                    sucesso: false
                    erro_codigo: EMITTER_CNPJ_REQUIRED
                    mensagem: Informe o CNPJ do emitente no header X-Emitente-CNPJ (14 caracteres).
                    request_id: d4e5f6789012345678abcdef0123456789
        '401':
          description: Token inválido ou inativo para o emitente informado
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiErrorResponse'
              example:
                sucesso: false
                erro_codigo: UNAUTHORIZED
                mensagem: Não autorizado para o emitente informado.
                request_id: e5f6789012345678abcdef0123456789012
        '405':
          description: Método não permitido (use POST)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiErrorResponse'
        '422':
          description: Array JSON vazio na raiz
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiErrorResponse'
              example:
                sucesso: false
                erro_codigo: EMPTY_ARRAY
                mensagem: Esperado um array JSON com ao menos um objeto de CT-e.
                request_id: f6789012345678abcdef012345678901234
        '500':
          description: Erro interno
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiErrorResponse'

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: |
        Token de integração (opaco). Alternativa: header `X-Embarcador-Token` com o mesmo valor (sem prefixo `Bearer`).
    embarcadorToken:
      type: apiKey
      in: header
      name: X-Embarcador-Token
      description: Mesmo token do Bearer; use um dos dois métodos de autenticação.

  parameters:
    EmitenteCnpj:
      name: X-Emitente-CNPJ
      in: header
      required: true
      description: |
        CNPJ da transportadora **emitente** do CT-e (14 caracteres: numérico ou alfanumérico, NT 2025.001).
        Aliases aceitos pelo servidor: `X-Emissor-CNPJ`, `X-Company-CNPJ`.
      schema:
        type: string
        pattern: '^[A-Z0-9]{12}[0-9]{2}$'
        example: "99999999000199"

  schemas:
    CteEmbarcadorRequest:
      type: array
      minItems: 1
      items:
        $ref: '#/components/schemas/CteEmbarcadorItem'

    CteEmbarcadorItem:
      type: object
      required:
        - Remetente
        - Destinatario
        - Tomador
        - ValorTotalFrete
        - DadosNotaFiscal
      properties:
        NumeroProtocolo:
          oneOf:
            - type: string
            - type: integer
          description: Identificador da integração; incluído no xObs do CT-e (aceita texto ou número).
          example: INT-2026-0001
        ValorTotalFrete:
          type: number
          format: double
          minimum: 0.01
          description: Valor total do frete (prestação de transporte).
        PesoTotal:
          type: number
          format: double
          description: Opcional. Se informado e maior que zero, substitui a soma de `PesoTotalNota` das notas.
        PesoDensidadeCubagemTotal:
          type: number
          description: Opcional. Peso cubado total da carga.
        ValorComposicaoFrete:
          type: array
          description: |
            Opcional. Detalhamento do frete por componente (código, descrição e valor).
            O total da prestação no CT-e é o campo `ValorTotalFrete`.
          items:
            $ref: '#/components/schemas/ValorComposicaoFreteItem'
        DadosCIOT:
          allOf:
            - $ref: '#/components/schemas/DadosCIOT'
            - description: Opcional.
        DadosVPO:
          allOf:
            - $ref: '#/components/schemas/DadosVPO'
            - description: Opcional.
        Observacoes:
          type: string
          maxLength: 2000
          description: Texto adicional no xObs
        xObs:
          type: string
          maxLength: 2000
        Remetente:
          $ref: '#/components/schemas/ParceiroEmissao'
        Destinatario:
          $ref: '#/components/schemas/ParceiroEmissao'
        Tomador:
          allOf:
            - $ref: '#/components/schemas/ParceiroEmissao'
            - description: |
                Tomador do serviço (pagador do frete). Informe o CNPJ/CPF de quem paga:
                igual ao remetente (tipo 0), ao destinatário (tipo 3) ou outro (tipo 4).
        Pagador:
          allOf:
            - $ref: '#/components/schemas/ParceiroEmissao'
            - description: Alias de `Tomador` (mesmo significado — pagador do frete).
        DadosNotaFiscal:
          type: array
          minItems: 1
          items:
            $ref: '#/components/schemas/NotaFiscalItem'

    DadosCIOT:
      type: object
      description: CIOT. Obrigatórios no objeto — `Numero`, `CNPJIPEF` e `CNPJ` ou `CPF`.
      required:
        - Numero
        - CNPJIPEF
      properties:
        Numero:
          type: string
          pattern: '^\d{12}$'
          description: Código CIOT (12 dígitos)
          example: "123456789012"
        CIOT:
          type: string
          pattern: '^\d{12}$'
        CNPJ:
          type: string
          description: CNPJ do responsável (14 caracteres). Informe `CNPJ` ou `CPF`.
          example: "11111111000191"
        CPF:
          type: string
          description: CPF do responsável (11 dígitos). Alternativa ao `CNPJ`.
        ciot_cnpj:
          type: string
        ciot_cpf:
          type: string
        CNPJIPEF:
          type: string
          description: CNPJ da Instituição de Pagamento Eletrônico do Frete (14 caracteres)
          example: "44444444000194"
        cnpj_ipef:
          type: string

    DadosVPO:
      type: object
      description: Vale pedágio. Obrigatórios no objeto — `CNPJFornecedor` e `Valor`.
      required:
        - CNPJFornecedor
        - Valor
      properties:
        NumeroCompra:
          type: string
          maxLength: 20
          description: IDVPO / nCompra
          example: "000000000001"
        nCompra:
          type: string
        VPO:
          type: string
        Valor:
          type: number
          format: double
          minimum: 0.01
          description: Valor do vale pedágio
        ValorValePedagio:
          type: number
          format: double
        CNPJFornecedor:
          type: string
          description: CNPJ da empresa fornecedora do vale (14 caracteres)
          example: "55555555000195"
        CNPJForn:
          type: string
        CNPJPg:
          type: string
          description: CNPJ do pagador do vale, se diferente do emitente
        CPFPg:
          type: string
        tpValePed:
          type: string
          enum: ['01', '02', '03', '99']
          description: "01=TAG, 02=Cupom, 03=Cartão, 99=Outros"

    ValorComposicaoFreteItem:
      type: object
      description: Componente do detalhamento de frete em `ValorComposicaoFrete`.
      properties:
        CodigoServico:
          type: integer
          description: Código do tipo de serviço/componente.
        DescricaoServico:
          type: string
          description: Descrição do componente (ex. FRETE PESO, GRIS, PEDAGIO).
        ValorServico:
          type: number
          format: double
          description: Valor do componente.

    ParceiroEmissao:
      type: object
      description: |
        Dados completos do participante para emissão do CT-e (usado em `Remetente`, `Destinatario` e `Tomador`/`Pagador`).
        Corresponde ao que o AppCTE envia no XML (`rem`, `dest` e `toma4` quando o tomador é "outros").
      required:
        - CNPJCPF
        - RazaoSocial
        - Rua
        - Numero
        - Bairro
        - Cep
      properties:
        CNPJCPF:
          type: string
          description: CPF (11 dígitos) ou CNPJ (14 caracteres, numérico ou alfanumérico). Pontuação opcional.
          example: "11111111000191"
        RazaoSocial:
          type: string
          description: Razão social ou nome.
          example: EMPRESA FICTICIA LTDA
        InscricaoEstadual:
          type: string
          description: Opcional. Vazio ou `ISENTO` quando não houver inscrição estadual.
          example: ISENTO
        TelefoneContato:
          type: string
          description: Opcional. Telefone de contato (somente dígitos na gravação).
        TipoIdentificacao:
          type: string
          description: Opcional. Informativo (ex. CNPJ). O documento fiscal é `CNPJCPF`.
        Cep:
          type: string
          description: CEP com 8 dígitos (com ou sem máscara).
          example: "01001000"
        Rua:
          type: string
          description: Logradouro (xLgr no XML).
          example: RUA FICTICIA UM
        Numero:
          type: string
          description: Número do imóvel. Use `S/N` se não houver número.
          example: "1000"
        Complemento:
          type: string
          description: Opcional.
        Bairro:
          type: string
          example: BAIRRO FICTICIO
        Cidade:
          type: string
          description: |
            Nome do município. Obrigatório informar este campo **e** `UnidadeFederacao` quando `CidadeIBGE` não for enviado.
          example: CIDADE FICTICIA A
        UnidadeFederacao:
          type: string
          minLength: 2
          maxLength: 2
          description: UF (2 letras).
          example: SP
        CidadeIBGE:
          type: string
          description: |
            Código IBGE do município (7 dígitos). Forma preferida de localização.
            Alternativa: `Cidade` + `UnidadeFederacao` cadastrados no AppCTE.
          example: "3550308"

    NotaFiscalItem:
      type: object
      required:
        - KeyNotaFiscal
      properties:
        KeyNotaFiscal:
          type: string
          pattern: '^\d{44}$'
          description: |
            Chave de acesso da NF-e (**44 dígitos** após remover formatação).
            Notas sem chave válida geram erro no item (`DATABASE_OR_RULE`).
        ValorTotalNota:
          type: number
          format: double
        PesoTotalNota:
          type: number
          format: double
        QuantidadeVolumes:
          type: number
          format: double
        NaturezaMercadoria:
          type: string
        NCM:
          type: string
        SerieNotaFiscal:
          type: string
        NumeroNotaFiscal:
          type: string
        CNPJCPFEmissorNotaFiscal:
          type: string
          description: Opcional. CNPJ/CPF do emissor da NF-e.
        MeioTransporte:
          type: string
          description: Opcional. Meio de transporte da carga na nota.
        TipoTransporteCarga:
          type: string
          description: Opcional. Tipo de transporte da carga.
        NumeroPedido:
          type: string
          description: Opcional. Número do pedido vinculado à nota.
        DataEmissao:
          type: string
          description: Opcional. Data de emissão da nota (formato livre ou ISO 8601).
        EspecieAcondicionamento:
          type: string
          description: Opcional. Espécie de acondicionamento da mercadoria.
        PesoDensidadeCubagem:
          type: number
          description: Opcional. Peso cubado da nota.
        NumeroProtocoloNotaFiscal:
          oneOf:
            - type: string
            - type: integer
          description: Opcional. Protocolo interno da nota no embarcador.

    CteEmbarcadorResponse:
      type: object
      required:
        - sucesso
        - mensagem
        - itens
        - request_id
      description: |
        Resposta do lote. Em sucesso parcial, HTTP continua **200** com `sucesso: false`.
        `company_id` é preenchido quando a autenticação foi aceita.
      properties:
        sucesso:
          type: boolean
          description: true apenas se **todos** os itens tiveram sucesso
        company_id:
          type: integer
          description: Identificador do emitente no AppCTE (informativo; o CNPJ de emissão é o header `X-Emitente-CNPJ`).
        mensagem:
          type: string
        request_id:
          type: string
          description: Identificador único da requisição — informe ao suporte em caso de problemas.
        itens:
          type: array
          items:
            $ref: '#/components/schemas/CteEmbarcadorItemResult'

    CteEmbarcadorItemResult:
      type: object
      properties:
        indice:
          type: integer
          description: Índice no array enviado (0-based)
        sucesso:
          type: boolean
        fase:
          type: string
          enum:
            - gravado
            - gravado_sem_transmissao
            - autorizado
            - transmissao_rejeitada
        erro_codigo:
          type: string
          description: |
            Código do erro, quando `sucesso` = false:
            `VALIDATION_ERROR` (dados inválidos), `INVALID_ITEM` (item do array malformado),
            `DATABASE_OR_RULE` (falha ao registrar ou regra de negócio), `SEFAZ_OR_API` (rejeição ou falha na autorização).
          enum:
            - VALIDATION_ERROR
            - INVALID_ITEM
            - DATABASE_OR_RULE
            - SEFAZ_OR_API
        mensagem:
          type: string
        cte_id:
          type: integer
          description: Identificador do CT-e no AppCTE (quando o documento foi registrado com sucesso).
        numero:
          type: integer
        serie:
          type: integer
        cfop:
          type: string
        chave:
          type: string
          nullable: true
          description: |
            Chave de acesso do CT-e (44 dígitos). Preenchida somente quando `fase` = `autorizado`
            (autorização na SEFAZ concluída com sucesso).
        protocolo:
          type: string
          nullable: true
          description: Número do protocolo de autorização (`nProt`). Mesmas regras de `chave`.
        detalhes:
          type: object
          properties:
            http_code:
              type: integer
            cStat:
              type: string
            xMotivo:
              type: string

    ApiErrorResponse:
      type: object
      properties:
        sucesso:
          type: boolean
          example: false
        erro_codigo:
          type: string
        mensagem:
          type: string
        request_id:
          type: string
        detalhe:
          type: string
          description: Detalhe adicional em falhas do servidor (erro HTTP 500).
