# Conota — contrato da API Endpoints, parâmetros e campos. Gerado a partir da especificação OpenAPI em https://api.conota.dev/docs/json e https://api.conota.dev/v1/captura/openapi.json (notas recebidas) — geradas do código. Nada aqui é escrito à mão. Base: https://api.conota.dev Autenticação: header `X-API-Key: ` ou `Authorization: Bearer `. > IMPORTANTE — em PARÂMETRO DE CAMINHO o CNPJ vai SEM A BARRA. A barra da máscara > é separador de caminho e a rota responde 404. Em query string e no corpo, a > máscara completa é aceita. Gerado em 2026-10-29. ============================================================================== ## NFS-e — Emissao, consulta e cancelamento de NFS-e ============================================================================== ### POST /v1/nfse-nacional/emitir Emitir NFS-e — Padrão Nacional, motor próprio (assíncrono) Enfileira a emissão de uma NFS-e no Padrão Nacional pelo motor próprio do Conota. A DPS é montada e validada contra o XSD oficial antes do envio, e cada erro de leiaute volta com o caminho do campo. Acompanhe o resultado por `GET /v1/nfse/consultar/{job_id}` ou pelo webhook. Consulta, arquivos e cancelamento usam as rotas `/v1/nfse` de sempre. Antes de emitir, confira o payload em `POST /v1/nfse-nacional/validar`, que não consome cota nem numeração. **Leiautes (`versao`)** | Valor | Leiaute | |---|---| | `1.01` | XSD oficial de 09/02/2026. É o padrão quando `versao` é omitido. | | `1.01-20261020` | XSD oficial publicado em 20/10/2026 para a Produção Restrita (homologação), com parte da NT 009. | | `1.01-nt009` | Leiaute completo da NT 009, com condomínios. Provisório até a publicação do XSD oficial. | **O que muda no leiaute `1.01-20261020`** - `finNFSe` sai de `IBSCBS` e vai para `infDPS`, logo depois de `tpEmit`. Passa a ser obrigatório. - `CST` e `cClassTrib` sobem de `IBSCBS.valores.trib.gIBSCBS` para `IBSCBS.valores.trib`. O `gIBSCBS` fica opcional e guarda só `cCredPres`, `gTribRegular` e `gDif`. - `cIndOp` passa a ser opcional. - A dedução/redução (`gDedRed`) e o reembolso (`gReeRepRes`) dão lugar ao ajuste da base de cálculo (`gAjusteBC`). - Novos grupos: `IBSCBS.imovel.gLocacao`, `IBSCBS.imovel.gUnidImob` e `IBSCBS.bensMoveis`. Um campo enviado na posição de outro leiaute é recusado com `campo não existe no leiaute`. Em produção, continue no `1.01` até a Sefin adotar o novo XSD. **Corpo (JSON)** — obrigatório - `cpf_cnpj` (string, OBRIGATÓRIO) — CNPJ/CPF do prestador. Aceita com ou sem máscara. CNPJ alfanumérico é aceito. - `nota` (object, OBRIGATÓRIO) — Objeto com `infDPS` no leiaute escolhido em `versao`. Quando omitidos, são preenchidos a partir do cadastro: prestador (CNPJ/CPF, IM e `regTrib`), `cLocEmi`, `tpAmb`, `dhEmi`, `dCompet`, `serie` e `nDPS`. Envie `"IM": null` para não informar a inscrição municipal; a Sefin recusa a IM (E0120) quando o município não tem dados do prestador no CNC. Com IBS/CBS, informe `serv.cServ.cNBS` e `IBSCBS.cIndOp` conforme a correlação item × NBS × IndOp × cClassTrib (Anexo VIII da NT 004). Dependendo do `cIndOp`, o endereço do tomador também é obrigatório (E0234). - `versao` (enum: "1.01" | "1.01-20261020" | "1.01-nt009") — Leiaute da DPS. Omitido, usa `1.01`. Ver a tabela de leiautes na descrição da rota. - `webhook_url` (string, máx 2048 chars) — URL https para notificação ao finalizar. Precisa ser https, em host público e porta padrão; redirecionamento não é seguido. - `referencia_externa` (string, máx 120 chars) — Opcional. Id único do seu lado (até 120 chars). Reenviar a MESMA referência devolve o job existente (HTTP 200, idempotente:true) em vez de reemitir. - (aceita campos adicionais: campo com nome desconhecido é IGNORADO em silêncio — use POST /v1/nfe/verificar-campos antes de emitir) **Exemplo** ```json { "cpf_cnpj": "string", "referencia_externa": "PEDIDO-12345", "versao": "1.01", "nota": { "infDPS": { "dCompet": "string", "toma": { "CPF": "string", "xNome": "string", "email": "string", "end": { "endNac": { "cMun": "string", "CEP": "string" }, "xLgr": "string", "nro": "string", "xBairro": "string" } }, "serv": { "locPrest": { "cLocPrestacao": "string" }, "cServ": { "cTribNac": "string", "xDescServ": "string", "cNBS": "string" } }, "valores": { "vServPrest": { "vServ": 0 }, "trib": { "tribMun": { "tribISSQN": "string", "tpRetISSQN": "string", "pAliq": 0 }, "totTrib": { "indTotTrib": "string" } } }, "IBSCBS": { "finNFSe": "string", "cIndOp": "string", "indDest": "string", "valores": { "trib": { "gIBSCBS": { "CST": "string", "cClassTrib": "string" } } } } } } } ``` **Respostas:** 200 ### POST /v1/nfse-nacional/condominios/emitir Emitir NFS-e de condomínio (subitem 99.05, NT 009) Mesma emissão do POST /v1/nfse-nacional/emitir, já no leiaute da NT 009, com os grupos IBSCBS.imovel e IBSCBS.condominios (cobranças, detalhamento e descontos). Antes do envio são conferidas as regras da NT: vServ = soma das cobranças, descontos batendo com vDescCondIncond, tipo de cobrança sem repetição e grupo do imóvel presente. **Corpo (JSON)** — obrigatório - `cpf_cnpj` (string, OBRIGATÓRIO) — CNPJ/CPF do prestador. Aceita com ou sem máscara. CNPJ alfanumérico é aceito. - `nota` (object, OBRIGATÓRIO) — Objeto com `infDPS` no leiaute escolhido em `versao`. Quando omitidos, são preenchidos a partir do cadastro: prestador (CNPJ/CPF, IM e `regTrib`), `cLocEmi`, `tpAmb`, `dhEmi`, `dCompet`, `serie` e `nDPS`. Envie `"IM": null` para não informar a inscrição municipal; a Sefin recusa a IM (E0120) quando o município não tem dados do prestador no CNC. Com IBS/CBS, informe `serv.cServ.cNBS` e `IBSCBS.cIndOp` conforme a correlação item × NBS × IndOp × cClassTrib (Anexo VIII da NT 004). Dependendo do `cIndOp`, o endereço do tomador também é obrigatório (E0234). - `versao` (enum: "1.01" | "1.01-20261020" | "1.01-nt009") — Leiaute da DPS. Omitido, usa o leiaute da NT 009. - `webhook_url` (string, máx 2048 chars) — URL https para notificação ao finalizar. Precisa ser https, em host público e porta padrão; redirecionamento não é seguido. - `referencia_externa` (string, máx 120 chars) — Opcional. Id único do seu lado (até 120 chars). Reenviar a MESMA referência devolve o job existente (HTTP 200, idempotente:true) em vez de reemitir. - (aceita campos adicionais: campo com nome desconhecido é IGNORADO em silêncio — use POST /v1/nfe/verificar-campos antes de emitir) **Exemplo** ```json { "cpf_cnpj": "string", "referencia_externa": "COND-UNID101-2026-12", "nota": { "infDPS": { "dCompet": "string", "finNFSe": "0", "indDest": "0", "toma": { "CPF": "string", "xNome": "string" }, "serv": { "locPrest": { "cLocPrestacao": "string" }, "cServ": { "cTribNac": "990501", "xDescServ": "string" } }, "valores": { "vServPrest": { "vServ": 715 }, "vDescCondIncond": { "vDescCond": 35.75 } }, "IBSCBS": { "imovel": { "cMun": "string", "gUnidImob": [ { "end": { "CEP": "string", "xLgr": "string", "nro": "string", "xCpl": "string" } } ] }, "condominios": { "dVencOrig": "string", "gCobranca": [ { "tpCobranca": "01", "vCobranca": 650 }, { "tpCobranca": "05", "vCobranca": 65 } ], "gDesconto": [ { "tpDesconto": "2", "vDesconto": 35.75, "xTpDesconto": "string" } ] }, "valores": { "trib": { "CST": "string", "cClassTrib": "string" } } } } } } ``` **Respostas:** 200 ### POST /v1/nfse-nacional/validar Validar DPS do Padrão Nacional (sem emitir) Monta a DPS como a emissão montaria e valida contra o XSD e as regras de condomínio, sem assinar, sem enviar e sem consumir cota ou numeração. Devolve o XML gerado e cada erro com o caminho do campo. Use o mesmo `versao` da emissão. **Leiautes (`versao`)** | Valor | Leiaute | |---|---| | `1.01` | XSD oficial de 09/02/2026. É o padrão quando `versao` é omitido. | | `1.01-20261020` | XSD oficial publicado em 20/10/2026 para a Produção Restrita (homologação), com parte da NT 009. | | `1.01-nt009` | Leiaute completo da NT 009, com condomínios. Provisório até a publicação do XSD oficial. | **O que muda no leiaute `1.01-20261020`** - `finNFSe` sai de `IBSCBS` e vai para `infDPS`, logo depois de `tpEmit`. Passa a ser obrigatório. - `CST` e `cClassTrib` sobem de `IBSCBS.valores.trib.gIBSCBS` para `IBSCBS.valores.trib`. O `gIBSCBS` fica opcional e guarda só `cCredPres`, `gTribRegular` e `gDif`. - `cIndOp` passa a ser opcional. - A dedução/redução (`gDedRed`) e o reembolso (`gReeRepRes`) dão lugar ao ajuste da base de cálculo (`gAjusteBC`). - Novos grupos: `IBSCBS.imovel.gLocacao`, `IBSCBS.imovel.gUnidImob` e `IBSCBS.bensMoveis`. Um campo enviado na posição de outro leiaute é recusado com `campo não existe no leiaute`. Em produção, continue no `1.01` até a Sefin adotar o novo XSD. **Corpo (JSON)** — obrigatório - `cpf_cnpj` (string, OBRIGATÓRIO) — CNPJ/CPF do prestador. Aceita com ou sem máscara. CNPJ alfanumérico é aceito. - `nota` (object, OBRIGATÓRIO) — Objeto com infDPS. - `versao` (enum: "1.01" | "1.01-20261020" | "1.01-nt009") — Leiaute da DPS. Omitido, usa `1.01`, ou `1.01-nt009` quando o `cTribNac` é de condomínio (99.05). - (aceita campos adicionais: campo com nome desconhecido é IGNORADO em silêncio — use POST /v1/nfe/verificar-campos antes de emitir) **Respostas:** 200 ### POST /v1/nfse/emitir Emitir NFS-e — provedor Nacional (assincrono) Enfileira a emissao de uma NFS-e no Padrao Nacional. O corpo "nota" usa o formato infDPS. Retorna um job_id; acompanhe via GET /nfse/consultar/{id} ou webhook. **Corpo (JSON)** — obrigatório - `cpf_cnpj` (string, OBRIGATÓRIO) — CNPJ/CPF do prestador. Aceita com ou sem máscara. **CNPJ alfanumérico (NT Conjunta 2025.001) é aceito** — 12 posições alfanuméricas + 2 dígitos verificadores, ex.: `00.000.000/E08G-12`. - `nota` (object, OBRIGATÓRIO) — Objeto com ambiente, provedor:"nacional" e infDPS (veja a Referencia de Campos na documentacao). - `webhook_url` (string, máx 2048 chars) — URL https para notificacao ao finalizar. Precisa ser https, em host publico e porta padrao; redirecionamento nao e seguido. - `referencia_externa` (string, máx 120 chars) — Opcional. Id unico do seu lado (letras/numeros, ate 120 chars; ex.: UUID). Idempotente por tenant: reenviar a MESMA referencia devolve o job existente (HTTP 200, idempotente:true) em vez de reemitir a nota. Para corrigir e reemitir uma nota que falhou, use uma NOVA referencia. Tambem permite localizar a emissao depois via GET /v1/jobs/localizar?ref=... mesmo sem o job_id. - (aceita campos adicionais: campo com nome desconhecido é IGNORADO em silêncio — use POST /v1/nfe/verificar-campos antes de emitir) **Exemplo** ```json { "cpf_cnpj": "string", "referencia_externa": "PEDIDO-12345", "nota": { "ambiente": "string", "provedor": "nacional", "infDPS": { "tpAmb": 0, "dhEmi": "string", "dCompet": "string", "prest": { "CNPJ": "string", "IM": "string", "opSimpNac": 0 }, "toma": { "CPF": "string", "xNome": "string", "end": { "endNac": { "cMun": "string", "CEP": "string" }, "xLgr": "string", "nro": "string", "xBairro": "string" } }, "serv": { "locPrest": { "cLocPrestacao": "string" }, "cServ": { "cTribNac": "string", "xDescServ": "string", "cNBS": "string" } }, "valores": { "vServPrest": { "vServ": 0 }, "trib": { "tribMun": { "tribISSQN": 0, "cLocIncid": "string", "vBC": 0, "tpRetISSQN": 0, "vLiq": 0 }, "totTrib": { "vTotTrib": { "vTotTribFed": 0, "vTotTribEst": 0, "vTotTribMun": 0 } } } }, "IBSCBS": { "finNFSe": 0, "indFinal": 0, "cIndOp": "string", "indDest": 0, "valores": { "trib": { "gIBSCBS": { "CST": "string", "cClassTrib": "string" } } } } } } } ``` **Respostas:** 200 ### POST /v1/nfse/govdigital/emitir Emitir NFS-e — provedor GovDigital (assincrono) Enfileira a emissao de uma NFS-e no GovDigital (Tecnos/NFe-Cidades). Mesmo formato infDPS do endpoint nacional, com provedor:"govdigital". **Corpo (JSON)** — obrigatório - `cpf_cnpj` (string, OBRIGATÓRIO) — CNPJ/CPF do prestador. Aceita com ou sem máscara. **CNPJ alfanumérico (NT Conjunta 2025.001) é aceito** — 12 posições alfanuméricas + 2 dígitos verificadores, ex.: `00.000.000/E08G-12`. - `nota` (object, OBRIGATÓRIO) — Objeto com ambiente, provedor:"govdigital" e infDPS (veja a Referencia de Campos na documentacao). - `webhook_url` (string, máx 2048 chars) — URL https para notificacao ao finalizar. Precisa ser https, em host publico e porta padrao; redirecionamento nao e seguido. - `referencia_externa` (string, máx 120 chars) — Opcional. Id unico do seu lado (letras/numeros, ate 120 chars; ex.: UUID). Idempotente por tenant: reenviar a MESMA referencia devolve o job existente (HTTP 200, idempotente:true) em vez de reemitir a nota. Para corrigir e reemitir uma nota que falhou, use uma NOVA referencia. Tambem permite localizar a emissao depois via GET /v1/jobs/localizar?ref=... mesmo sem o job_id. - (aceita campos adicionais: campo com nome desconhecido é IGNORADO em silêncio — use POST /v1/nfe/verificar-campos antes de emitir) **Exemplo** ```json { "cpf_cnpj": "string", "referencia_externa": "PEDIDO-12345", "nota": { "ambiente": "string", "provedor": "govdigital", "infDPS": { "tpAmb": 0, "dhEmi": "string", "dCompet": "string", "prest": { "CNPJ": "string", "IM": "string", "opSimpNac": 0, "end": { "endNac": { "cMun": "string", "CEP": "string" }, "xLgr": "string", "nro": "string", "xBairro": "string" } }, "toma": { "CPF": "string", "xNome": "string", "end": { "endNac": { "cMun": "string", "CEP": "string" }, "xLgr": "string", "nro": "string", "xBairro": "string" } }, "serv": { "locPrest": { "cLocPrestacao": "string" }, "cServ": { "cTribNac": "string", "xDescServ": "string", "cNBS": "string" } }, "valores": { "vServPrest": { "vServ": 0 }, "trib": { "tribMun": { "tribISSQN": 0, "cLocIncid": "string", "vBC": 0, "pAliq": 0, "vISSQN": 0, "tpRetISSQN": 0, "vLiq": 0 }, "totTrib": { "vTotTrib": { "vTotTribFed": 0, "vTotTribEst": 0, "vTotTribMun": 0 } } } }, "IBSCBS": { "finNFSe": 0, "indFinal": 0, "cIndOp": "string", "indDest": 0, "valores": { "trib": { "gIBSCBS": { "CST": "string", "cClassTrib": "string" } } } } } } } ``` **Respostas:** 200 ### POST /v1/nfse/cancelar Cancelar NFS-e Solicita o cancelamento de uma NFS-e emitida. Informe job_id (da emissao) OU numero, e a justificativa. **Corpo (JSON)** — obrigatório - `cpf_cnpj` (string, OBRIGATÓRIO) - `job_id` (string) - `numero` (integer) - `codigo_cancelamento` (string) - `justificativa` (string) - `webhook_url` (string, máx 2048 chars) — URL https para notificacao ao finalizar. Precisa ser https, em host publico e porta padrao; redirecionamento nao e seguido. - (aceita campos adicionais: campo com nome desconhecido é IGNORADO em silêncio — use POST /v1/nfe/verificar-campos antes de emitir) **Exemplo** ```json { "cpf_cnpj": "string", "job_id": "string", "justificativa": "string" } ``` **Respostas:** 200 ### GET /v1/nfse/consultar/{id} Consultar job de NFS-e Retorna o status e o resultado de um job de NFS-e (status, dados da nota, arquivos disponiveis). **Parâmetros (path)** - `id` (string, OBRIGATÓRIO) — ID do job (UUID) **Respostas:** 200 ### GET /v1/nfse/{id}/file/{tipo} Baixar arquivo da NFS-e Download direto do arquivo (binario) de um job de NFS-e. O `pdf` aqui e o DANFSe. Nao sabe o que o job produziu? GET /v1/jobs/{id}/arquivos lista o que existe. Job de cancelamento tem xml_cancelamento e log, mas NAO tem xml. **Parâmetros (path)** - `id` (string, OBRIGATÓRIO) — ID do job - `tipo` (enum: "xml" | "xml_cancelamento" | "pdf" | "log", OBRIGATÓRIO) — xml, pdf, log ou xml_cancelamento **Respostas:** 200 ### POST /v1/nfse/consultar-sefaz Consultar NFS-e na SEFAZ/prefeitura (tempo real) Consulta a nota diretamente no webservice da prefeitura/SEFAZ. Informe chave_acesso, job_id ou numero. **Corpo (JSON)** — obrigatório - `cpf_cnpj` (string, OBRIGATÓRIO) - `chave_acesso` (string) - `job_id` (string) - `numero` (integer) - `tipo_consulta` (string) - (aceita campos adicionais: campo com nome desconhecido é IGNORADO em silêncio — use POST /v1/nfe/verificar-campos antes de emitir) **Respostas:** 200 ============================================================================== ## NF-e — Emissao, consulta e cancelamento de NF-e ============================================================================== ### POST /v1/nfe/emitir Emitir NF-e (assincrono) Enfileira a emissao de uma NF-e. Retorna um job_id; acompanhe via GET /nfe/consultar/{id} ou webhook. **Corpo (JSON)** — obrigatório - `cpf_cnpj` (string, OBRIGATÓRIO) — CNPJ/CPF do emitente. Aceita com ou sem máscara. **CNPJ alfanumérico (NT Conjunta 2025.001) é aceito** — 12 posições alfanuméricas + 2 dígitos verificadores, ex.: `00.000.000/E08G-12`. - `nota` (object, OBRIGATÓRIO) — Dados da NF-e: identificacao, emitente, destinatario, produtos[], total, pagamentos[]... (veja a Referencia de Campos na documentacao). REFORMA TRIBUTARIA (IBS/CBS/IS): por item, envie o grupo produtos[].ibscbs { cst, classificacao (cClassTrib), ibs_uf/ibs_mun/cbs: {valor_bc, aliquota, valor} } e, no nivel da nota, ibscbs_total. IMPORTANTE: para itens IMUNES/isentos (ex.: livro, CST 410) envie APENAS { cst, classificacao } — SEM os sub-grupos ibs_uf/ibs_mun/cbs e SEM ibscbs_total (o CST de imunidade veda o grupo de calculo). Imposto Seletivo vai em produtos[].is_ e is_total. INFORMACOES COMPLEMENTARES (OBS): envie em informacoes_adicionais { infCpl, infAdFisco } (ate 5000 caracteres no infCpl). O XML nao aceita quebra de linha: cada \n vira "; ". Para o DANFE quebrar a linha nesses pontos, envie danfe_config { quebrar_linha_obs: true } dentro de nota. - `webhook_url` (string, máx 2048 chars) — URL https para notificacao ao finalizar. Precisa ser https, em host publico e porta padrao; redirecionamento nao e seguido. - `referencia_externa` (string, máx 120 chars) — Opcional. Id unico do seu lado (letras/numeros, ate 120 chars; ex.: UUID). Idempotente por tenant: reenviar a MESMA referencia devolve o job existente (HTTP 200, idempotente:true) em vez de reemitir a nota. Para corrigir e reemitir uma nota que falhou, use uma NOVA referencia. Tambem permite localizar a emissao depois via GET /v1/jobs/localizar?ref=... mesmo sem o job_id. - (aceita campos adicionais: campo com nome desconhecido é IGNORADO em silêncio — use POST /v1/nfe/verificar-campos antes de emitir) **Exemplo** ```json { "cpf_cnpj": "string", "referencia_externa": "PEDIDO-12345", "nota": { "identificacao": { "natOp": "string", "mod": 0, "serie": 0, "nNF": 0, "dhEmi": "string", "tpNF": 0, "idDest": 0, "cMunFG": 0, "tpImp": 0, "tpEmis": 0, "finNFe": 0, "indFinal": 0, "indPres": 0, "procEmi": 0, "verProc": "string" }, "emitente": { "CNPJCPF": "string", "xNome": "string", "xFant": "string", "CRT": 0, "IE": "string", "xLgr": "string", "nro": "string", "xBairro": "string", "cMun": 0, "xMun": "string", "UF": "string", "cUF": 0, "CEP": "string", "cPais": 0, "xPais": "string", "Fone": "string" }, "destinatario": { "CNPJCPF": "string", "xNome": "string", "indIEDest": 0, "xLgr": "string", "nro": "string", "xBairro": "string", "cMun": 0, "xMun": "string", "UF": "string", "CEP": "string" }, "produtos": [ { "cProd": "string", "cEAN": "string", "xProd": "string", "NCM": "string", "CFOP": 0, "uCom": "string", "qCom": 0, "vUnCom": 0, "vProd": 0, "cEANTrib": "string", "uTrib": "string", "qTrib": 0, "vUnTrib": 0, "indTot": 0, "icms": { "orig": 0, "CST": "string" }, "pis": { "CST": "string" }, "cofins": { "CST": "string" }, "ibscbs": { "cst": "string", "classificacao": "string", "ibs_uf": { "valor_bc": 0, "aliquota": 0, "valor": 0 }, "ibs_mun": { "valor_bc": 0, "aliquota": 0, "valor": 0 }, "cbs": { "valor_bc": 0, "aliquota": 0, "valor": 0 } } } ], "total": { "vProd": 0, "vNF": 0 }, "ibscbs_total": { "vBCIBSCBS": 0, "ibs": { "vIBS": 0, "uf": { "vIBSUF": 0 }, "mun": { "vIBSMun": 0 } }, "cbs": { "vCBS": 0 } }, "transportador": { "modFrete": 0 }, "pagamentos": [ { "tPag": "string", "vPag": 0, "indPag": 0 } ] } } ``` **Respostas:** 200 ### POST /v1/nfe/cancelar Cancelar NF-e Solicita o cancelamento de uma NF-e autorizada. Informe job_id (da emissao) OU chave, e a justificativa (min. 15 caracteres). **Corpo (JSON)** — obrigatório - `cpf_cnpj` (string, OBRIGATÓRIO) - `job_id` (string) - `chave` (string) - `justificativa` (string) - `webhook_url` (string, máx 2048 chars) — URL https para notificacao ao finalizar. Precisa ser https, em host publico e porta padrao; redirecionamento nao e seguido. - (aceita campos adicionais: campo com nome desconhecido é IGNORADO em silêncio — use POST /v1/nfe/verificar-campos antes de emitir) **Exemplo** ```json { "cpf_cnpj": "string", "job_id": "string", "justificativa": "string" } ``` **Respostas:** 200 ### GET /v1/nfe/consultar/{id} Consultar job de NF-e Retorna o status e o resultado de um job de NF-e (status, dados da nota, arquivos disponiveis). **Parâmetros (path)** - `id` (string, OBRIGATÓRIO) — ID do job (UUID) **Respostas:** 200 ### GET /v1/nfe/{id}/file/{tipo} Baixar arquivo da NF-e Download direto do arquivo (binario) de um job de NF-e. O `pdf` aqui e o DANFE em A4. Nao sabe o que o job produziu? GET /v1/jobs/{id}/arquivos lista o que existe. Job de cancelamento tem xml_cancelamento e log, mas NAO tem xml. **Parâmetros (path)** - `id` (string, OBRIGATÓRIO) — ID do job - `tipo` (enum: "xml" | "xml_cancelamento" | "pdf" | "log", OBRIGATÓRIO) — xml, pdf, log ou xml_cancelamento **Respostas:** 200 ============================================================================== ## NFC-e — Emissao, consulta e cancelamento de NFC-e (modelo 65) ============================================================================== ### POST /v1/nfce/emitir Emitir NFC-e (assincrono) Enfileira a emissao de uma NFC-e (modelo 65). Requer CSC configurado (PUT /empresas/{cpf_cnpj}/nfce). O modelo (65), tpImp (4), idDest (1) e indPres (1) sao aplicados automaticamente. Retorna job_id; acompanhe via GET /nfce/consultar/{id} ou webhook. **Corpo (JSON)** — obrigatório - `cpf_cnpj` (string, OBRIGATÓRIO) — CNPJ/CPF do emitente. Aceita com ou sem máscara. **CNPJ alfanumérico (NT Conjunta 2025.001) é aceito** — 12 posições alfanuméricas + 2 dígitos verificadores, ex.: `00.000.000/E08G-12`. - `nota` (object, OBRIGATÓRIO) — Dados da NFC-e: identificacao, emitente, produtos[], total, pagamentos[] (destinatario opcional). - `webhook_url` (string, máx 2048 chars) - `referencia_externa` (string, máx 120 chars) — Opcional. Id unico do seu lado (letras/numeros, ate 120 chars; ex.: UUID). Idempotente por tenant: reenviar a MESMA referencia devolve o job existente (HTTP 200, idempotente:true) em vez de reemitir a nota. Para corrigir e reemitir uma nota que falhou, use uma NOVA referencia. Tambem permite localizar a emissao depois via GET /v1/jobs/localizar?ref=... mesmo sem o job_id. - (aceita campos adicionais: campo com nome desconhecido é IGNORADO em silêncio — use POST /v1/nfe/verificar-campos antes de emitir) **Exemplo** ```json { "cpf_cnpj": "string", "referencia_externa": "PEDIDO-12345", "nota": { "identificacao": { "modelo": 65, "serie": 0, "nNF": 0, "dhEmi": "string", "natOp": "string", "tpNF": 1, "idDest": 1, "cMunFG": 0, "tpImp": 4, "tpEmis": 1, "finNFe": 1, "indFinal": 1, "indPres": 1 }, "emitente": { "CNPJCPF": "string", "xNome": "string", "xFant": "string", "CRT": 0, "IE": "string", "xLgr": "string", "nro": "string", "xBairro": "string", "cMun": 0, "xMun": "string", "UF": "string", "cUF": 0, "CEP": "string" }, "produtos": [ { "cProd": "string", "cEAN": "SEM GTIN", "xProd": "string", "NCM": "string", "CFOP": 0, "uCom": "string", "qCom": 0, "vUnCom": 0, "vProd": 0, "cEANTrib": "SEM GTIN", "uTrib": "string", "qTrib": 0, "vUnTrib": 0, "indTot": 1, "icms": { "orig": 0, "CST": "string" }, "pis": { "CST": "string" }, "cofins": { "CST": "string" } } ], "total": { "vProd": 0, "vNF": 0 }, "pagamentos": [ { "tPag": "string", "vPag": 0 } ] } } ``` **Respostas:** 200 ### POST /v1/nfce/cancelar Cancelar NFC-e Solicita o cancelamento de uma NFC-e autorizada. Informe job_id (da emissao) OU chave, e a justificativa (min. 15 caracteres). **Corpo (JSON)** — obrigatório - `cpf_cnpj` (string, OBRIGATÓRIO) - `job_id` (string) - `chave` (string) - `justificativa` (string) - `webhook_url` (string, máx 2048 chars) — URL https para notificacao ao finalizar. Precisa ser https, em host publico e porta padrao; redirecionamento nao e seguido. - (aceita campos adicionais: campo com nome desconhecido é IGNORADO em silêncio — use POST /v1/nfe/verificar-campos antes de emitir) **Exemplo** ```json { "cpf_cnpj": "string", "job_id": "string", "justificativa": "string" } ``` **Respostas:** 200 ### GET /v1/nfce/consultar/{id} Consultar job de NFC-e Retorna o status e o resultado de um job de NFC-e (status, dados da nota, arquivos disponiveis). **Parâmetros (path)** - `id` (string, OBRIGATÓRIO) — ID do job (UUID) **Respostas:** 200 ### GET /v1/nfce/{id}/file/{tipo} Baixar arquivo da NFC-e Download direto do arquivo (binario) de um job de NFC-e. O `pdf` aqui e o DANFE NFC-e em CUPOM de 80 mm, nao o A4 da NF-e. Nao sabe o que o job produziu? GET /v1/jobs/{id}/arquivos lista o que existe. Job de cancelamento tem xml_cancelamento e log, mas NAO tem xml. **Parâmetros (path)** - `id` (string, OBRIGATÓRIO) — ID do job - `tipo` (enum: "xml" | "xml_cancelamento" | "pdf" | "log", OBRIGATÓRIO) — xml, pdf, log ou xml_cancelamento **Respostas:** 200 ============================================================================== ## CT-e — Emissao, consulta e cancelamento de CT-e (modelo 57) e CT-e OS (modelo 67), modal rodoviario ============================================================================== ### POST /v1/cte/emitir Emitir CT-e (assincrono) Enfileira a emissao de um **CT-e (modelo 57)** ou **CT-e OS (modelo 67)**, modal rodoviario. O modelo vem de `nota.identificacao.modelo`; o padrao e 57. Os dois tem estruturas diferentes: - **57** move carga de terceiros e tem cinco papeis: `remetente`, `expedidor`, `recebedor`, `destinatario` e o `tomador`, que e **escolhido entre eles** por `tomador.indice` (0=remetente, 1=expedidor, 2=recebedor, 3=destinatario). - **67** e servico prestado a um contratante unico: so `tomador`, e `servico` no lugar de `carga`. O `rntrc` (57) e o `taf` (67) saem da configuracao da empresa quando nao vierem no payload - configure em `PUT /v1/empresas/{cpf_cnpj}/cte`. Datas podem ir em ISO 8601; a conversao para o formato que o autorizador exige e nossa. Retorna `job_id`; acompanhe por `GET /v1/cte/consultar/{id}` ou por webhook. **Respostas:** `202` enfileirado - `400` payload invalido - `404` empresa nao encontrada - `422` certificado ou configuracao ausente - `429` cota esgotada - `403` ambiente incompativel. **Corpo (JSON)** — obrigatório - `cpf_cnpj` (string, OBRIGATÓRIO) — CNPJ do transportador emitente - `nota` (object, OBRIGATÓRIO) — Dados do CT-e. - `referencia_externa` (string, máx 120 chars) — Seu identificador. Reenviar a mesma referencia devolve o job original em vez de emitir de novo. - `webhook_url` (string) — Endereco que recebe o resultado quando a emissao terminar. **Respostas:** 200 ### POST /v1/cte/cancelar Cancelar CT-e Solicita o cancelamento de um CT-e autorizado. Informe `job_id` (da emissao) OU `chave`, mais a justificativa de no minimo 15 caracteres. O prazo de cancelamento do CT-e e definido por cada UF e **costuma ser mais curto que o da NF-e**; varios estados nao permitem cancelar depois de iniciada a prestacao do servico. Quem recusa e a SEFAZ, nao a API. Consome cota de CT-e. **Corpo (JSON)** — obrigatório - `cpf_cnpj` (string, OBRIGATÓRIO) - `job_id` (string) — ID do job da emissao - `chave` (string, máx 44 chars) — Chave de acesso do CT-e - `justificativa` (string, máx 255 chars, OBRIGATÓRIO) - `webhook_url` (string) **Respostas:** 200 ### GET /v1/cte/consultar/{id} Consultar job de CT-e Retorna o status e o resultado de um job de CT-e. Le o que ja guardamos: **nao consome cota**. **Parâmetros (path)** - `id` (string, OBRIGATÓRIO) — ID do job **Respostas:** 200 ### GET /v1/cte/{id}/file/{tipo} Baixar arquivo do CT-e Download direto do arquivo de um job de CT-e. O `pdf` e o **DACTE**. Nao sabe o que o job produziu? `GET /v1/jobs/{id}/arquivos` lista o que existe. **Parâmetros (path)** - `id` (string, OBRIGATÓRIO) — ID do job - `tipo` (enum: "xml" | "xml_cancelamento" | "pdf" | "log", OBRIGATÓRIO) **Respostas:** 200 ============================================================================== ## MDF-e — Emissao, encerramento e cancelamento do Manifesto Eletronico de Documentos Fiscais (modelo 58) ============================================================================== ### POST /v1/mdfe/emitir Emitir MDF-e (assincrono) Enfileira a emissao de um **MDF-e (modelo 58)** -- o manifesto que acompanha a viagem. `tipo_emitente` decide **qual documento o manifesto carrega**, e vem da configuracao da empresa quando nao enviado: - `1` prestador de servico de transporte -> manifesto de **CT-e** - `2` transportador de carga propria -> manifesto de **NF-e** - `3` prestador com CT-e Globalizado -> manifesto de **NF-e** A combinacao errada **nao da erro no autorizador**: ele monta um manifesto sem documento nenhum. O Conota recusa antes, com o motivo. Os documentos vao em `descarregamento[]`, agrupados pelo municipio onde saem da carga. > **Manifesto aberto e infracao.** Depois que a viagem termina, use `POST /v1/mdfe/encerrar`. Para saber quais seguem abertos, `POST /v1/mdfe/nao-encerrados` pergunta direto a SEFAZ. **Respostas:** `202` enfileirado - `400` payload invalido - `404` empresa nao encontrada - `422` certificado ou configuracao ausente - `429` cota esgotada. **Corpo (JSON)** — obrigatório - `cpf_cnpj` (string, OBRIGATÓRIO) — CNPJ do emitente do manifesto - `nota` (object, OBRIGATÓRIO) — Dados do MDF-e. - `referencia_externa` (string, máx 120 chars) - `webhook_url` (string) **Respostas:** 200 ### POST /v1/mdfe/encerrar Encerrar MDF-e Encerra o manifesto quando a viagem termina. **Manifesto aberto e infracao** -- e o unico documento do Conota que exige acao depois de autorizado. O `codigo_municipio` e onde a viagem **de fato** acabou, que nem sempre e o destino planejado. Consome cota de MDF-e. **Corpo (JSON)** — obrigatório - `cpf_cnpj` (string, OBRIGATÓRIO) - `chave` (string, máx 44 chars, OBRIGATÓRIO) - `data` (string) — Data do encerramento. Padrao: hoje. - `codigo_municipio` (string, OBRIGATÓRIO) — IBGE do municipio onde a viagem terminou - `protocolo` (string, OBRIGATÓRIO) — Protocolo de autorizacao do manifesto - `webhook_url` (string) **Respostas:** 200 ### POST /v1/mdfe/cancelar Cancelar MDF-e Cancela um manifesto autorizado. So vale **antes de iniciada a viagem**; depois disso o caminho e o encerramento, nao o cancelamento. Justificativa de no minimo 15 caracteres. Consome cota de MDF-e. **Corpo (JSON)** — obrigatório - `cpf_cnpj` (string, OBRIGATÓRIO) - `chave` (string, máx 44 chars, OBRIGATÓRIO) - `justificativa` (string, máx 255 chars, OBRIGATÓRIO) - `webhook_url` (string) **Respostas:** 200 ### POST /v1/mdfe/nao-encerrados Listar MDF-e nao encerrados Pergunta **a propria SEFAZ** quais manifestos deste CNPJ seguem abertos. Manifesto aberto e infracao, e esta e a fonte da verdade: melhor perguntar a ela que manter um espelho e arriscar divergir. Assincrona como o resto: devolve `job_id`, e o resultado sai em `GET /v1/mdfe/consultar/{id}`. **Nao consome cota.** **Corpo (JSON)** — obrigatório - `cpf_cnpj` (string, OBRIGATÓRIO) - `webhook_url` (string) **Respostas:** 200 ### GET /v1/mdfe/consultar/{id} Consultar job de MDF-e Retorna o status e o resultado de um job de MDF-e. Le o que ja guardamos: **nao consome cota**. **Parâmetros (path)** - `id` (string, OBRIGATÓRIO) **Respostas:** 200 ### GET /v1/mdfe/{id}/file/{tipo} Baixar arquivo do MDF-e Download do arquivo de um job de MDF-e. O `pdf` e o **DAMDFE**, que acompanha o veiculo. **Parâmetros (path)** - `id` (string, OBRIGATÓRIO) - `tipo` (enum: "xml" | "xml_cancelamento" | "pdf" | "log", OBRIGATÓRIO) **Respostas:** 200 ============================================================================== ## CEP — Consulta de CEP ============================================================================== ### GET /v1/cep/{cep} Consultar endereco por CEP Retorna o endereco completo a partir de um CEP (8 digitos). **Parâmetros (path)** - `cep` (string, OBRIGATÓRIO) — CEP (somente numeros) **Respostas:** 200 ### GET /v1/cep Buscar CEP por logradouro Busca CEPs a partir de logradouro / cidade / UF. **Parâmetros (query)** - `logradouro` (string, OBRIGATÓRIO) - `tipo` (string) - `bairro` (string) - `cidade` (string) - `uf` (string) **Respostas:** 200 ============================================================================== ## CNPJ — Consulta de CNPJ ============================================================================== ### GET /v1/cnpj/{cnpj} Consultar dados cadastrais por CNPJ Retorna os dados cadastrais de uma empresa a partir do CNPJ. **Parâmetros (path)** - `cnpj` (string, OBRIGATÓRIO) — CNPJ **sem a barra**: `00000000000191` ou `00.000.0000001-91`. A barra da máscara é separador de caminho na URL — enviando `00.000.000/0001-91` a rota não é encontrada (404). Aceita **CNPJ alfanumérico** (NT Conjunta 2025.001): 12 posições alfanuméricas + 2 dígitos verificadores. **Respostas:** 200 ============================================================================== ## NCM — Consulta da tabela NCM por codigo ou por descricao do produto ============================================================================== ### POST /v1/ncm/buscar Buscar NCM pela descricao do produto Procura NCMs pela **descricao do produto**. E a consulta que resolve o problema real de quem cadastra produto: voce sabe que o item e um *parafuso*, nao que ele e o *7318.15.00*. Devolve os codigos candidatos com a descricao oficial e a vigencia, para escolher com seguranca. O termo e procurado em **qualquer posicao** da descricao: `autoperfurantes` encontra *Parafusos autoperfurantes*. Use `so_inicio` para restringir ao comeco da descricao, quando a busca ampla trouxer resultado demais. Por padrao a resposta traz **apenas codigos de 8 digitos** -- os unicos que servem numa nota. A tabela tambem tem os niveis da arvore (`7318` e um agrupamento, nao um NCM emitivel); para ve-los, envie `apenas_codigos_finais: false`. Busca sem resultado devolve `200` com `ncms: []`. Nao e erro. > A tabela vem do portal do governo e fica em cache aqui. A primeira consulta depois de uma atualizacao pode demorar um pouco mais. **Corpo (JSON)** — obrigatório - `descricao` (string, OBRIGATÓRIO) — Texto a procurar na descricao oficial. Ex.: "parafuso", "cafe soluvel". - `limite` (integer, min 1) — Maximo de resultados (padrao 50). - `so_inicio` (boolean) — Exige que a descricao COMECE com o termo. Padrao `false` (procura em qualquer posicao). - `apenas_codigos_finais` (boolean) — Devolve so os NCMs de 8 digitos, que sao os validos numa nota. Padrao `true`. **Respostas:** 200 ### GET /v1/ncm/{codigo} Consultar NCM por codigo Devolve a descricao oficial, a vigencia e o ato normativo de um NCM de 8 digitos. **Parâmetros (path)** - `codigo` (string, OBRIGATÓRIO) — NCM de 8 digitos, com ou sem pontuacao **Respostas:** 200 ### GET /v1/ncm/{codigo}/validar Validar um codigo NCM Responde apenas se o codigo existe na tabela vigente. Util antes de emitir, quando o NCM vem de cadastro antigo. **Parâmetros (path)** - `codigo` (string, OBRIGATÓRIO) **Respostas:** 200 ============================================================================== ## Notas — Listagem, consulta e auditoria de notas ============================================================================== ### POST /v1/jobs/consultar-lote Consulta o status de vários jobs de uma vez (lote) Envie uma lista de `job_ids` (máx. 200) e receba o status de cada um numa só chamada. Escopo por tenant: ids inexistentes ou de outro tenant retornam `status: "nao_encontrado"`. A ordem do retorno segue a ordem enviada. **Corpo (JSON)** — obrigatório - `job_ids` (array, OBRIGATÓRIO) — Lista de job_id (UUID) a consultar. **Exemplo** ```json { "job_ids": [ "3fa85f64-5717-4562-b3fc-2c963f66afa6", "9c8b1e2d-0a4f-4c3b-8e5a-1d2f3a4b5c6d" ] } ``` **Respostas:** 200 ### GET /v1/jobs/erros Lista jobs com erro (status failed) num período, por data Retorna os jobs que **falharam** (`status = failed`) num intervalo de datas, escopado por tenant. Útil para reconciliar falhas de emissão em lote (ex.: automatizar retry). - **Período**: `de`/`ate` no formato `YYYY-MM-DD` (data de criação do job, fuso de Brasília). A janela é de no **máximo 90 dias**. Se você **não** informar datas, retorna os **últimos 7 dias**; se informar só `ate`, `de` = `ate − 7 dias`. - **`formato=lista`** (padrão): lista paginada de jobs. **`formato=resumo`**: contagem agregada por tipo e motivo normalizado (sem listar cada job). - Só falhas de **job**. Notas que foram autorizadas mas **rejeitadas** pela SEFAZ/prefeitura NÃO entram aqui. - O campo `erro` vem saneado (sem marcadores internos); quando houve reemissão, o novo id vem em `reemitido_como`. **Parâmetros (query)** - `de` (string, padrão ^\d{4}-\d{2}-\d{2}$) — Data inicial (YYYY-MM-DD). Default: `ate − 7 dias`. Ex.: "2026-06-18". - `ate` (string, padrão ^\d{4}-\d{2}-\d{2}$) — Data final (YYYY-MM-DD). Default: hoje. Ex.: "2026-06-19". - `tipo` (enum: "nfe" | "nfse" | "nfce" | "mdfe") — Filtra por tipo de documento. - `cpf_cnpj` (string) — Filtra por emitente (CNPJ; pode enviar com ou sem máscara). - `formato` (enum: "lista" | "resumo") — `lista` (paginada) ou `resumo` (agregado). - `pagina` (integer, default 1, min 1) — Página (formato=lista). - `por_pagina` (integer, default 100, min 1) — Itens por página (máx. 500). **Respostas:** 200 ### GET /v1/jobs/rejeitadas Lista notas rejeitadas pela SEFAZ/prefeitura num período, por data Retorna as notas cujo job foi processado mas o documento voltou **rejeitado** (`status = rejeitada`) num intervalo de datas, escopado por tenant. É o **par** de `GET /v1/jobs/erros`: aquela cobre jobs que **falharam** (nunca viraram nota); esta cobre notas que **foram rejeitadas** pela SEFAZ/prefeitura. - **Período**: `de`/`ate` no formato `YYYY-MM-DD` (data de criação do registro, fuso de Brasília). Janela de no **máximo 90 dias**. Sem datas → **últimos 7 dias**; só `ate` → `de` = `ate − 7 dias`. - **`formato=lista`** (padrão): lista paginada de notas. **`formato=resumo`**: contagem agregada por tipo e `codigo_status` (o código da SEFAZ é a chave do motivo). - Só `rejeitada`. Cancelamento é ação do cliente (rota própria); denegação/inutilização não entram aqui. **Parâmetros (query)** - `de` (string, padrão ^\d{4}-\d{2}-\d{2}$) — Data inicial (YYYY-MM-DD). Default: `ate − 7 dias`. Ex.: "2026-07-11". - `ate` (string, padrão ^\d{4}-\d{2}-\d{2}$) — Data final (YYYY-MM-DD). Default: hoje. Ex.: "2026-07-18". - `tipo` (enum: "nfe" | "nfse" | "nfce" | "mdfe") — Filtra por tipo de documento. - `cpf_cnpj` (string) — Filtra por emitente (CNPJ; com ou sem máscara). - `formato` (enum: "lista" | "resumo") — `lista` (paginada) ou `resumo` (agregado). - `pagina` (integer, default 1, min 1) — Página (formato=lista). - `por_pagina` (integer, default 100, min 1) — Itens por página (máx. 500). **Respostas:** 200 ### GET /v1/jobs/localizar Localizar job/emissão sem o job_id (por referência ou atributos) Recupera o `job_id` (ou confirma que a emissão nunca aconteceu) quando o cliente **perdeu o job_id**. Escopado por tenant. Duas formas de busca: 1. **Por referência** — se você enviou `referencia_externa` no `/emitir`, basta `?ref=` (busca exata, sem precisar de período). 2. **Por atributos** — informe um **período** (`de`/`ate`, máx. 90 dias; sem datas → últimos 7 dias) e **pelo menos um** de: `cpf_cnpj` (emitente), `destinatario_cpf_cnpj` ou valor (`valor` exato, ou faixa `valor_min`/`valor_max`). Refine com `tipo` e `status`. A busca cruza os dados do próprio job (referência, destinatário e valor denormalizados na criação) com a nota fiscal, quando existir. **0 resultados** = forte indício de que não processou (pode reemitir com segurança); **1** = é o job perdido; **N** = desambigúe por destinatário/valor/hora. Nunca retorna o payload (que carrega dados sensíveis). > Observação: destinatário/valor passam a ser gravados no job a partir de 18/07/2026; emissões anteriores são encontradas por esses campos apenas quando viraram nota (ou após backfill). **Parâmetros (query)** - `ref` (string, máx 120 chars) — A `referencia_externa` que você enviou no /emitir (busca exata). Dispensa período. Ex.: "3fa85f64-5717-4562-b3fc-2c963f66afa6". - `de` (string, padrão ^\d{4}-\d{2}-\d{2}$) — Data inicial (YYYY-MM-DD). Default: `ate − 7 dias` (quando busca por atributos). - `ate` (string, padrão ^\d{4}-\d{2}-\d{2}$) — Data final (YYYY-MM-DD). Default: hoje. - `cpf_cnpj` (string) — CNPJ do emitente (com ou sem máscara). - `destinatario_cpf_cnpj` (string) — CPF/CNPJ do destinatário (com ou sem máscara). - `valor` (number) — Valor total exato do documento. - `valor_min` (number) — Piso do valor (faixa). - `valor_max` (number) — Teto do valor (faixa). - `tipo` (enum: "nfe" | "nfse" | "nfce" | "mdfe") — Filtra por tipo de documento. - `status` (enum: "pending" | "processing" | "completed" | "failed" | "cancelled") — Filtra pelo status do job. - `pagina` (integer, default 1, min 1) - `por_pagina` (integer, default 100, min 1) **Respostas:** 200 ### GET /v1/jobs/{job_id}/itens Itens (produtos/serviços) de uma emissão, com os grupos tributários Retorna os **itens** de uma emissão a partir do `job_id`, com código, descrição, NCM, CFOP, quantidade, valores e os **grupos tributários de cada item** (ICMS, PIS, COFINS e **IBS/CBS**). ### Para que serve Responder **"qual produto derrubou minha nota"** sem abrir XML. Quando o fisco rejeita apontando um item (`[nItem: N]` no motivo), a rota marca esse item com `"rejeitado": true` e o repete em `item_rejeitado` — o culpado sai identificado em **uma** chamada. ### De onde vêm os dados Do **payload enviado** na emissão (`"fonte": "payload_enviado"`), não do XML autorizado. Isso é deliberado: o XML só existe para nota autorizada, enquanto o payload existe para **qualquer status** — inclusive as rejeitadas e as que falharam, que são justamente as que você precisa diagnosticar. Para notas autorizadas o conteúdo é equivalente (a SEFAZ não altera itens); se você precisa do documento com valor legal, baixe o XML em `/v1/{tipo}/{job_id}/file/xml`. ### Por tipo de documento - **NF-e** e **NFC-e**: um item por produto, na ordem em que foram enviados (`nItem` começa em 1). - **NFS-e**: a DPS tem **um único serviço**, devolvido como um item só — com `codigo_tributacao_nacional`, `codigo_nbs` e o grupo `issqn`. > Nunca devolve o payload cru (que carrega certificado e senha) — apenas os itens e seus grupos fiscais. ### Exemplo de uso Uma NF-e rejeitada com *"Aliquota do IBS da UF invalida[nItem: 2]"* devolve `item_rejeitado` com o código e a descrição do produto, e o `ibscbs` daquele item mostra o que foi enviado (ex.: `cst: "000"` com alíquota `0` — contradição que o fisco recusa, já que CST 000 é tributação integral). **Parâmetros (path)** - `job_id` (string, OBRIGATÓRIO) — ID do job retornado na emissão. Se você o perdeu, recupere em `GET /v1/jobs/localizar`. Ex.: "0fafe732-9665-4d36-8971-c8063962290c". **Respostas:** 200 ### POST /v1/nfe/verificar-campos Verificar campos do payload antes de emitir (não emite nada) Recebe **o mesmo corpo que você enviaria para `/v1/nfe/emitir`** e devolve os campos que o emissor **não reconhece** — sem emitir, sem consumir numeração e **sem debitar cota**. ### O problema que isto resolve Campo com nome errado **não gera erro**: ele é ignorado na conversão e o campo correto sai vazio ou zerado. Um `vlrProd` no lugar de `vProd` produz uma NF-e formalmente válida com o produto valendo **R$ 0,00** — e você só descobre pela rejeição do fisco, ou não descobre. ### O que a resposta traz Para cada campo desconhecido: o **caminho exato** dentro do payload e, quando existe algo parecido, uma **sugestão**: ```json { "caminho": "nota.produtos[0].vlrProd", "campo": "vlrProd", "sugestao": "vProd" } ``` ### Duas camadas 1. **Nomes de campo** — sempre, só com o payload. 2. **Schema oficial** — quando você informa `cpf_cnpj`: monta, assina com o certificado do emitente e valida contra o XSD da NF-e, **sem transmitir**. Pega valor malformado (`NCM` de 3 dígitos, data inválida, tamanho excedido). As duas se complementam e **nenhuma substitui a outra**: campo ignorado não chega ao XML, então o schema não o enxerga; e nome certo não garante valor válido. ### Limites — leia antes de confiar - **Regra fiscal não é verificada** (ex.: CST 000 com alíquota zerada). Isso só o fisco avalia. - Sem `cpf_cnpj`, apenas a camada 1 roda — a resposta diz isso em `schema.verificado: false`. - Disponível para **NF-e e NFC-e**. A **NFS-e usa outro layout** e ainda não tem vocabulário próprio — pedir `tipo: "nfse"` é recusado em vez de acusar campos legítimos como desconhecidos. **Corpo (JSON)** — obrigatório - `nota` (object, OBRIGATÓRIO) — O mesmo objeto `nota` do /emitir. - `tipo` (enum: "nfe" | "nfce") — Tipo do documento. - `cpf_cnpj` (string) — CNPJ do emitente. **Opcional**: informando, roda também a validação contra o schema oficial (precisa do certificado para assinar antes de validar). **Respostas:** 200 ### GET /v1/arquivos/zips Listar zips mensais de XMLs disponíveis Lista os pacotes ZIP mensais de XMLs do tenant, gerados automaticamente no dia 1º de cada mês (um zip por tipo de documento — nfe/nfse/nfce — com todos os XMLs de emissão do mês anterior, organizados por CNPJ do emitente + manifest.json). Retenção: 15 meses. `completo: false` indica zip gerado antes do fechamento do mês (parcial; será regenerado no dia 1º). Baixe via `GET /v1/arquivos/zip-mensal`. **Respostas:** 200 ### GET /v1/arquivos/zip-mensal Download do zip mensal de XMLs (URL assinada) Retorna a URL de download (válida por 1h) do ZIP mensal de XMLs do tenant para o tipo e a competência informados. Os zips são gerados no dia 1º de cada mês com os XMLs do mês anterior e mantidos por 15 meses. Use `GET /v1/arquivos/zips` para ver os disponíveis. **Parâmetros (query)** - `tipo` (enum: "nfe" | "nfse" | "nfce", OBRIGATÓRIO) — Tipo de documento fiscal. Ex.: "nfe". - `competencia` (string, padrão ^\d{4}-\d{2}$, OBRIGATÓRIO) — Mês de referência (YYYY-MM). Ex.: "2026-07". **Respostas:** 200 ### GET /v1/notas Listar notas fiscais Lista as notas fiscais do tenant (NF-e, NFS-e, NFC-e), da mais recente para a mais antiga (ordena por `data_emissao`, depois `criado_em`). Escopada ao tenant autenticado. - **Filtros** (todos opcionais e combináveis por E): `tipo` (tipo de documento), `status` (situação fiscal) e `cpf_cnpj` (CNPJ do **emitente**). Sem filtros, retorna todas as notas do tenant. - **Paginação**: `limite` (padrão 20) e `pagina` (padrão 1). A resposta traz `total` (contagem total do filtro), `pagina` e `limite`. - **Cada item** inclui: identificação (`tipo`, `modelo`, `serie`, `numero`, `chave_acesso`); situação (`status`, `codigo_status`, `motivo_status`, `protocolo`, `data_autorizacao`); `valor_total`, destinatário (`destinatario_nome`, `destinatario_cpf_cnpj`) e emitente (`emitente_nome`, `emitente_cpf_cnpj`); dados de cancelamento (`protocolo_cancelamento`, `data_cancelamento`, `justificativa_cancelamento`); e os ids de origem `job_emissao_id` / `job_cancelamento_id` + `ambiente`. - **Rotas relacionadas**: status de um job → `GET /v1//consultar/{id}`; documento por chave → `GET /v1/notas/{chave}`; reconciliação por período → `GET /v1/jobs/erros` e `GET /v1/jobs/rejeitadas`. **Parâmetros (query)** - `tipo` (enum: "nfe" | "nfse" | "nfce" | "mdfe") — Filtra por tipo de documento. Ex.: "nfe". - `status` (enum: "autorizada" | "cancelada" | "rejeitada" | "denegada" | "inutilizada") — Filtra pela situação fiscal da nota. Ex.: "autorizada". - `cpf_cnpj` (string) — CNPJ do emitente. Aceita com ou sem máscara. **CNPJ alfanumérico (NT Conjunta 2025.001) é aceito** — 12 posições alfanuméricas + 2 dígitos verificadores, ex.: `00.000.000/E08G-12`. Ex.: "11222333000181". - `limite` (integer, default 20, min 1) — Itens por página (padrão 20). - `pagina` (integer, default 1, min 1) — Página, começando em 1 (padrão 1). **Respostas:** 200 ### GET /v1/notas/rejeicoes Relatório de emissões × rejeições (resumo com valores e motivos) Resumo consolidado do período: **emitidas com sucesso** (autorizadas + canceladas) × **com erro** (notas rejeitadas + jobs falhos), com **valores somados**, **percentuais**, classificação por **categoria** (fiscal, duplicidade, infra), os **principais motivos** e um resumo por tipo (NF-e/NFS-e). Escopado ao tenant. É a visão de topo — use `GET /v1/notas/rejeicoes/detalhe` (por código/motivo) e `GET /v1/notas/emitidas/detalhe` para o drill-down paginado. Sem datas → do 1º dia do mês corrente até hoje. **Parâmetros (query)** - `de` (string) — Data inicial YYYY-MM-DD. Default: 1º dia do mês atual. Ex.: "2026-07-01". - `ate` (string) — Data final YYYY-MM-DD. Default: hoje. Ex.: "2026-07-18". - `tipo` (enum: "nfe" | "nfse" | "nfce" | "mdfe") — Filtra por tipo de documento. **Respostas:** 200 ### GET /v1/notas/rejeicoes/detalhe Detalhe paginado de rejeições/falhas (drill-down por código ou motivo) Drill-down paginado e buscável de um item do relatório `GET /v1/notas/rejeicoes`. Informe **`codigo`** (código numérico da SEFAZ — ex.: 225 = falha de schema, 539 = duplicidade → notas `rejeitada`) **ou** **`motivo`** (texto normalizado do erro, para falhas de **job** NFS-e/pré-SEFAZ, quando `codigo` = `-`). Um dos dois é obrigatório (senão `400`). Escopado ao tenant. Sem datas → do 1º dia do mês até hoje. **Parâmetros (query)** - `codigo` (string) — Código de status da SEFAZ (ex.: 225, 539). Use "-" para falhas de job (aí informe `motivo`). Ex.: "539". - `motivo` (string) — Motivo normalizado do erro (quando `codigo` = "-"). Ex.: "Certificado vencido". - `tipo` (enum: "nfe" | "nfse" | "nfce" | "mdfe") — Filtra por tipo de documento. - `busca` (string) — Filtra por nome/CPF/CNPJ do destinatário ou data (DD/MM/YYYY HH:MM). Ex.: "Maria". - `de` (string) — Data inicial YYYY-MM-DD. Default: 1º dia do mês. Ex.: "2026-07-01". - `ate` (string) — Data final YYYY-MM-DD. Default: hoje. Ex.: "2026-07-18". - `pagina` (integer, default 1) — Página. - `limite` (integer, default 20) — Itens por página (máx. 100). **Respostas:** 200 ### GET /v1/notas/emitidas/detalhe Detalhe paginado de notas emitidas com sucesso Lista paginada e buscável das notas **emitidas com sucesso** (status `autorizada` ou `cancelada`) no período — o drill-down do bloco "emitidas" do relatório `GET /v1/notas/rejeicoes`. Escopado ao tenant. Ordena da mais recente para a mais antiga. Sem datas → do 1º dia do mês corrente até hoje. **Parâmetros (query)** - `de` (string) — Data inicial YYYY-MM-DD. Default: 1º dia do mês. Ex.: "2026-07-01". - `ate` (string) — Data final YYYY-MM-DD. Default: hoje. Ex.: "2026-07-18". - `tipo` (enum: "nfe" | "nfse" | "nfce" | "mdfe") — Filtra por tipo de documento. - `busca` (string) — Filtra por nome/CPF/CNPJ do destinatário ou data (DD/MM/YYYY HH:MM). Ex.: "Maria". - `pagina` (integer, default 1) — Página. - `limite` (integer, default 20) — Itens por página (máx. 100). **Respostas:** 200 ### GET /v1/notas/auditoria Auditoria por documento/nome, Job ID ou referência externa Busca notas e jobs do tenant por **um** destes critérios: `busca` (documento/nome do destinatário), `job_id` (completo ou prefixo) ou `referencia_externa` (a que você enviou no `/emitir`, correspondência exata). Sempre escopado ao tenant. Retorna as notas do período (autorizada/rejeitada/cancelada) e — em modo `job_id`/`referencia_externa` — também o job quando ainda **não virou nota** (falha/pendente), com resumo por status e valores. Datas (`de`/`ate`) são opcionais. **Parâmetros (query)** - `busca` (string) — CPF/CNPJ ou nome do destinatário (aceita máscara). Ex.: "Maria Silva". - `job_id` (string) — ID do job (completo ou prefixo). Ex.: "8881bac5". - `referencia_externa` (string) — Referência externa exata enviada no /emitir. Ex.: "PEDIDO-12345". - `de` (string) — Data inicial YYYY-MM-DD (opcional). Ex.: "2026-07-01". - `ate` (string) — Data final YYYY-MM-DD (opcional). Ex.: "2026-07-18". - `pagina` (integer, default 1) — Página. - `limite` (integer, default 20) — Itens por página (máx. 100). **Respostas:** 200 ### GET /v1/notas/{chave} Consultar nota por chave de acesso **Parâmetros (path)** - `chave` (string, OBRIGATÓRIO) — Chave de acesso (44 digitos) **Respostas:** 200 ============================================================================== ## Empresas — Cadastro e configuracao de empresas emissoras ============================================================================== ### GET /v1/empresas Listar empresas emissoras do tenant **Respostas:** 200 ### POST /v1/empresas Cadastrar empresa emissora Cria uma empresa emissora na conta. É o **primeiro passo** de qualquer integração: sem empresa cadastrada não há o que emitir. Cadastrar não basta para emitir. Depois disto faltam ainda: 1. enviar o certificado A1 — `PUT /v1/empresas/{cpf_cnpj}/certificado`; 2. configurar o tipo de documento — `PUT /v1/empresas/{cpf_cnpj}/nfe`, `/nfse` ou `/nfce`. **CNPJs são ilimitados para emissão** — cadastre quantas empresas precisar. > O `cpf_cnpj` é gravado **sem máscara**, independente de como você enviar. É por ele que a empresa é referenciada nas demais rotas. **Respostas:** `201` com a empresa criada · `400` falta `cpf_cnpj` ou `nome_razao_social` · `409` empresa já cadastrada nesta conta · `500` erro interno. **Corpo (JSON)** — obrigatório - `cpf_cnpj` (string, OBRIGATÓRIO) — CNPJ ou CPF do emitente. Com ou sem máscara — aqui vai no corpo, então a barra não atrapalha. CNPJ alfanumérico é aceito. - `nome_razao_social` (string, OBRIGATÓRIO) — Razão social, exatamente como consta no CNPJ. - `nome_fantasia` (string) — Nome fantasia. - `email` (string) — E-mail de contato da empresa. - `fone` (string) — Telefone, só dígitos com DDD. - `inscricao_estadual` (string) — Inscrição estadual. Obrigatória para NF-e e NFC-e; use `ISENTO` quando aplicável. - `inscricao_municipal` (string) — Inscrição municipal. Exigida por vários municípios na NFS-e. - `endereco` (object) — Endereço do emitente. O `codigo_municipio` (IBGE) é o que determina o município emissor na NFS-e. **Exemplo** ```json { "cpf_cnpj": "11222333000181", "nome_razao_social": "string", "nome_fantasia": "string", "email": "string", "fone": "string", "inscricao_estadual": "string", "inscricao_municipal": "string", "endereco": { "cep": "string", "logradouro": "string", "numero": "string", "bairro": "string", "cidade": "string", "uf": "string", "codigo_municipio": "string" } } ``` **Respostas:** 200 ### GET /v1/empresas/certificados Listar certificados digitais do tenant (validade e status) Retorna o certificado A1 ativo de cada empresa do tenant — titular, emissor, validade e status: valido | expira_em_breve (<= 30 dias) | expirado | sem_certificado. **Respostas:** 200 ### GET /v1/empresas/numeracao Último número autorizado por empresa, tipo, série e ambiente Diz **em que número cada sequência está** — o número da última nota **autorizada**, por empresa, tipo de documento, série e ambiente. Homologação e produção são sequências **independentes**, e vêm separadas. Nota rejeitada não avança a sequência: só entram autorizadas. Informe `cnpj` para trazer só uma empresa. Sem ele, vêm todas as do tenant. **Isto é consulta, não reserva.** Serve para saber onde a numeração está — não para atribuir o próximo número: entre ler e emitir há uma janela, e duas emissões simultâneas leriam o mesmo valor. Quem numera continua sendo você. **Parâmetros (query)** - `cnpj` (string) — Opcional — CNPJ/CPF da empresa (com ou sem máscara). - `tipo` (enum: "nfe" | "nfce" | "nfse") — Opcional — filtra por tipo. **Respostas:** 200 ### GET /v1/empresas/{cpf_cnpj} Detalhe de uma empresa **Parâmetros (path)** - `cpf_cnpj` (string, OBRIGATÓRIO) — CNPJ/CPF da empresa, **sem a barra**: `11222333000181` ou `11.222.333.0001-81`. A barra da máscara é separador de caminho na URL — enviando `11.222.333/0001-81` a rota não é encontrada (404). Em query string e no corpo, a máscara completa é aceita normalmente. CNPJ alfanumérico é aceito. Ex.: "11222333000181". **Respostas:** 200 ### PUT /v1/empresas/{cpf_cnpj} Atualizar dados cadastrais da empresa Atualiza os dados cadastrais. O `cpf_cnpj` **não muda** por aqui — ele identifica a empresa; para outro CNPJ, cadastre outra empresa. Campo não enviado **mantém o valor atual**. > Mudança de razão social, endereço ou regime costuma exigir atualização aqui **antes** da próxima emissão — o documento leva estes dados, e divergência com o cadastro na Receita ou na prefeitura gera rejeição. Configuração fiscal (ambiente, série, CSC) não vem aqui: use as rotas `/nfe`, `/nfse` e `/nfce`. **Respostas:** `200` com a empresa atualizada · `404` empresa não encontrada nesta conta · `500` erro interno. **Parâmetros (path)** - `cpf_cnpj` (string, OBRIGATÓRIO) — CNPJ/CPF da empresa, **sem a barra**: `11222333000181` ou `11.222.333.0001-81`. A barra da máscara é separador de caminho na URL — enviando `11.222.333/0001-81` a rota não é encontrada (404). Em query string e no corpo, a máscara completa é aceita normalmente. CNPJ alfanumérico é aceito. Ex.: "11222333000181". **Corpo (JSON)** — obrigatório - `nome_razao_social` (string) — Razão social, como consta no CNPJ. - `nome_fantasia` (string) - `email` (string) - `fone` (string) — Só dígitos, com DDD. - `inscricao_estadual` (string) — Use `ISENTO` quando aplicável. - `inscricao_municipal` (string) - `endereco` (object) — Endereço completo. Enviando este objeto, envie-o inteiro. **Respostas:** 200 ### DELETE /v1/empresas/{cpf_cnpj} Remover empresa **Parâmetros (path)** - `cpf_cnpj` (string, OBRIGATÓRIO) — CNPJ/CPF da empresa, **sem a barra**: `11222333000181` ou `11.222.333.0001-81`. A barra da máscara é separador de caminho na URL — enviando `11.222.333/0001-81` a rota não é encontrada (404). Em query string e no corpo, a máscara completa é aceita normalmente. CNPJ alfanumérico é aceito. Ex.: "11222333000181". **Respostas:** 200 ### PUT /v1/empresas/{cpf_cnpj}/nfe Configurar parâmetros de NF-e da empresa Define ambiente, regime tributário, série e numeração da NF-e desta empresa. **Obrigatório antes da primeira emissão** — sem esta configuração, `POST /v1/nfe/emitir` responde `422`. É um **upsert parcial**: envie só o que quer mudar. Campo não enviado **mantém o valor atual** — não volta para o padrão. | Campo | Padrão na criação | |---|---| | `ambiente` | `2` (homologação) | | `crt` | `1` (Simples Nacional) | | `serie` | `"001"` | | `proximo_numero` | `1` | > **`proximo_numero` não é um contador automático.** Ele é o valor de partida que você registra aqui; a emissão usa o `nNF` que **você** envia no payload. Para saber em que número cada série realmente está, use `GET /v1/empresas/numeracao`, que lê a última nota autorizada. > **Homologação e produção são sequências independentes.** Trocar o `ambiente` não reinicia nem transfere numeração. **Respostas:** `200` com a configuração salva · `404` empresa não encontrada nesta conta · `500` erro interno. **Parâmetros (path)** - `cpf_cnpj` (string, OBRIGATÓRIO) — CNPJ/CPF da empresa, **sem a barra**: `11222333000181` ou `11.222.333.0001-81`. A barra da máscara é separador de caminho na URL — enviando `11.222.333/0001-81` a rota não é encontrada (404). Em query string e no corpo, a máscara completa é aceita normalmente. CNPJ alfanumérico é aceito. Ex.: "11222333000181". **Corpo (JSON)** — obrigatório - `ambiente` (enum: 1 | 2) — Ambiente do autorizador: `1` = produção (valor fiscal real), `2` = homologação (sem valor fiscal). - `crt` (enum: 1 | 2 | 3 | 4) — Código do Regime Tributário: `1` = Simples Nacional, `2` = Simples Nacional com excesso de sublimite, `3` = Regime Normal, `4` = MEI. Precisa refletir o enquadramento REAL da empresa — ele muda a tributação do documento. - `serie` (string, máx 3 chars) — Série da NF-e, até 3 caracteres. Ex.: `"001"`. Cada série tem a sua própria numeração. - `proximo_numero` (integer, min 1) — Número de partida da série. Registro seu — a emissão usa o `nNF` enviado no payload. **Exemplo** ```json { "ambiente": 2, "crt": 1, "serie": "001", "proximo_numero": 1 } ``` **Respostas:** 200 ### PUT /v1/empresas/{cpf_cnpj}/nfse Configurar parâmetros de NFS-e da empresa Define ambiente, numeração de RPS, enquadramentos municipais e as credenciais da prefeitura. **Obrigatório antes da primeira emissão** de NFS-e. É um **upsert parcial**: envie só o que quer mudar. Campo não enviado **mantém o valor atual**. > **Atenção: aqui `ambiente` é TEXTO.** Nesta rota vale `"producao"` ou `"homologacao"` — diferente das rotas de NF-e e NFC-e, onde `ambiente` é o número `1` ou `2`. É a divergência que mais causa erro na configuração inicial. > **A configuração é POR AMBIENTE.** Homologação e produção têm registros separados, com **contadores de RPS independentes**. Configurar produção não altera o que está em homologação. | Campo | Padrão na criação | |---|---| | `ambiente` | `"homologacao"` | | `lote` | `1` | | `serie` | `"RPS"` | | `numero` | `1` | | `op_simp_nac` | `1` | | `reg_ap_trib_sn` | `0` | | `reg_esp_trib` | `0` | | `incentivo_fiscal` | `false` | **Credenciais de prefeitura** (`prefeitura_login`, `prefeitura_senha`, `prefeitura_token`) só são exigidas por municípios com sistema próprio. No Padrão Nacional, o certificado digital basta. Elas nunca são devolvidas nas consultas. **Respostas:** `200` com a configuração salva · `404` empresa não encontrada nesta conta · `500` erro interno. **Parâmetros (path)** - `cpf_cnpj` (string, OBRIGATÓRIO) — CNPJ/CPF da empresa, **sem a barra**: `11222333000181` ou `11.222.333.0001-81`. A barra da máscara é separador de caminho na URL — enviando `11.222.333/0001-81` a rota não é encontrada (404). Em query string e no corpo, a máscara completa é aceita normalmente. CNPJ alfanumérico é aceito. Ex.: "11222333000181". **Corpo (JSON)** — obrigatório - `ambiente` (enum: "producao" | "homologacao") — TEXTO, não número: `"producao"` ou `"homologacao"`. Cada ambiente tem a sua própria configuração e o seu próprio contador de RPS. - `lote` (integer, min 1) — Número do lote de envio. Contador próprio do emitente. - `serie` (string) — Série do RPS. Ex.: `"RPS"`. - `numero` (integer, min 1) — Número do RPS. É um contador **monotônico**: não reaproveite número já usado, mesmo após rejeição. - `op_simp_nac` (integer) — Optante pelo Simples Nacional. ATENÇÃO: a implementação atual espera `4` = não optante (o schema oficial usa `1`). Confirme o valor aceito antes de configurar um emitente novo. - `reg_ap_trib_sn` (integer) — Regime de apuração tributária pelo Simples Nacional. `0` quando não se aplica. - `reg_esp_trib` (integer) — Regime especial de tributação. `0` = nenhum. Campo distinto de `reg_ap_trib_sn` — cada um preenche a sua tag no XML. - `incentivo_fiscal` (boolean) — Empresa possui incentivo fiscal municipal. - `prefeitura_login` (string) — Login na prefeitura. Só para municípios com sistema próprio. - `prefeitura_senha` (string) — Senha na prefeitura. Nunca é devolvida nas consultas. - `prefeitura_token` (string) — Token da prefeitura, quando o município usa token em vez de senha. **Exemplo** ```json { "ambiente": "homologacao", "lote": 1, "serie": "RPS", "numero": 1, "op_simp_nac": 1, "reg_ap_trib_sn": 0, "reg_esp_trib": 0, "incentivo_fiscal": false } ``` **Respostas:** 200 ### PUT /v1/empresas/{cpf_cnpj}/nfce Configurar parametros de NFC-e da empresa (CSC, serie, ambiente) Define o CSC (Codigo de Seguranca do Contribuinte) e o ID do CSC, obtidos no portal da SEFAZ do estado da empresa - necessarios para gerar o QR Code da NFC-e. ambiente: 1=producao, 2=homologacao. crt: 1=Simples Nacional, 2=Simples excesso sublimite, 3=Regime Normal, 4=MEI. Por seguranca, o CSC nao e retornado; a resposta traz apenas csc_configurado (boolean). **Parâmetros (path)** - `cpf_cnpj` (string, OBRIGATÓRIO) — CNPJ/CPF da empresa, **sem a barra**: `11222333000181` ou `11.222.333.0001-81`. A barra da máscara é separador de caminho na URL — enviando `11.222.333/0001-81` a rota não é encontrada (404). Em query string e no corpo, a máscara completa é aceita normalmente. CNPJ alfanumérico é aceito. Ex.: "11222333000181". **Corpo (JSON)** — obrigatório - `ambiente` (enum: 1 | 2) — 1=producao, 2=homologacao - `crt` (enum: 1 | 2 | 3 | 4) - `serie` (string, máx 3 chars) - `proximo_numero` (integer, min 1) - `csc_id` (string, máx 10 chars) — ID do CSC / idToken (ex: "000001") - `csc` (string, máx 50 chars) — Codigo de Seguranca do Contribuinte (token) **Exemplo** ```json { "ambiente": 2, "crt": 1, "serie": "001", "proximo_numero": 1, "csc_id": "string", "csc": "string" } ``` **Respostas:** 200 ### PUT /v1/empresas/{cpf_cnpj}/cte Configurar parametros de CT-e da empresa (RNTRC, serie, ambiente) Define os parametros de emissao de **CT-e (modelo 57)** e **CT-e OS (modelo 67)**, ambos no modal rodoviario. `rntrc` — Registro Nacional de Transportadores Rodoviarios de Carga, obtido na ANTT. Vai no grupo `rodo` do CT-e (57). `taf` — Termo de Autorizacao de Fretamento, usado pelo CT-e OS (67). Quem so emite o 57 nao precisa. ambiente: 1=producao, 2=homologacao. crt: 1=Simples Nacional, 2=Simples excesso sublimite, 3=Regime Normal, 4=MEI. Campo nao enviado **mantem o valor atual** — nao volta ao padrao. **Respostas:** `200` configuracao salva · `404` empresa nao encontrada · `500` erro interno. **Parâmetros (path)** - `cpf_cnpj` (string, OBRIGATÓRIO) — CNPJ/CPF da empresa, **sem a barra**: `11222333000181` ou `11.222.333.0001-81`. A barra da máscara é separador de caminho na URL — enviando `11.222.333/0001-81` a rota não é encontrada (404). Em query string e no corpo, a máscara completa é aceita normalmente. CNPJ alfanumérico é aceito. Ex.: "11222333000181". **Corpo (JSON)** — obrigatório - `ambiente` (enum: 1 | 2) — 1=producao, 2=homologacao - `crt` (enum: 1 | 2 | 3 | 4) — Regime tributario do emitente - `serie` (string, máx 3 chars) - `proximo_numero` (integer, min 1) - `rntrc` (string, máx 12 chars) — Registro na ANTT — grupo rodo do CT-e (57) - `taf` (string, máx 20 chars) — Termo de Autorizacao de Fretamento — grupo rodoOS do CT-e OS (67) **Exemplo** ```json { "ambiente": 2, "crt": 3, "serie": "1", "proximo_numero": 1, "rntrc": "12345678", "taf": "" } ``` **Respostas:** 200 ### PUT /v1/empresas/{cpf_cnpj}/mdfe Configurar parametros de MDF-e da empresa (tipo de emitente, RNTRC, serie) Define os parametros de emissao de **MDF-e (modelo 58)**, modal rodoviario. `tipo_emitente` e o campo que mais gera erro silencioso: ele decide **qual documento o manifesto aceita**. - `1` prestador de servico de transporte -> manifesto de **CT-e** - `2` transportador de carga propria -> manifesto de **NF-e** - `3` prestador com CT-e Globalizado -> manifesto de **NF-e** A combinacao errada nao da erro na biblioteca do autorizador: ela monta um manifesto **sem documento nenhum**. Por isso o Conota recusa antes. `rntrc` e o registro do transportador na ANTT. Campo nao enviado **mantem o valor atual**. **Respostas:** `200` configuracao salva - `404` empresa nao encontrada - `500` erro interno. **Parâmetros (path)** - `cpf_cnpj` (string, OBRIGATÓRIO) — CNPJ/CPF da empresa, **sem a barra**: `11222333000181`. Ex.: "11222333000181". **Corpo (JSON)** — obrigatório - `ambiente` (enum: 1 | 2) — 1=producao, 2=homologacao - `serie` (string, máx 3 chars) - `proximo_numero` (integer, min 1) - `rntrc` (string, máx 12 chars) — Registro na ANTT - `tipo_emitente` (enum: 1 | 2 | 3) — 1=prestador (CT-e), 2=carga propria (NF-e), 3=CT-e Globalizado (NF-e) **Exemplo** ```json { "ambiente": 2, "serie": "1", "proximo_numero": 1, "rntrc": "12345678", "tipo_emitente": 1 } ``` **Respostas:** 200 ### PUT /v1/empresas/{cpf_cnpj}/certificado Enviar ou substituir o certificado digital A1 (PFX) Envia o certificado **A1** (`.pfx`/`.p12`) da empresa, em base64, junto com a senha. O Conota lê o certificado, extrai titular, emissor, validade e número de série, e passa a usá-lo em todas as emissões daquela empresa. Substituir é a mesma chamada: o certificado anterior é desativado e o novo entra no lugar. > **Só A1.** O A3 depende de mídia física (token ou cartão) e não se aplica a uma emissão executada em servidor. > **Certificado vencido derruba TODAS as emissões da empresa** — não é rejeição pontual, é parada total, e chega sem aviso no dia do vencimento. Acompanhe por `GET /v1/empresas/certificados`, que traz `dias_para_expirar` e o status de cada empresa. Como gerar o base64: ```bash base64 -w 0 certificado.pfx # Linux certutil -encode certificado.pfx saida.txt # Windows ``` **Respostas:** `200` com os dados extraídos do certificado · `400` falta `certificado` ou `password` · `404` empresa não encontrada · `422` senha incorreta ou arquivo inválido · `500` erro interno. **Parâmetros (path)** - `cpf_cnpj` (string, OBRIGATÓRIO) — CNPJ/CPF da empresa, **sem a barra**: `11222333000181` ou `11.222.333.0001-81`. A barra da máscara é separador de caminho na URL — enviando `11.222.333/0001-81` a rota não é encontrada (404). Em query string e no corpo, a máscara completa é aceita normalmente. CNPJ alfanumérico é aceito. Ex.: "11222333000181". **Corpo (JSON)** — obrigatório - `certificado` (string, OBRIGATÓRIO) — Conteúdo do arquivo `.pfx`/`.p12` em **base64**, sem quebras de linha e sem o prefixo `data:`. - `password` (string, OBRIGATÓRIO) — Senha do certificado. É armazenada cifrada e nunca devolvida em nenhuma consulta. **Exemplo** ```json { "certificado": "MIIQxQIBAzCCEI8GCSqGSIb3...", "password": "string" } ``` **Respostas:** 200 ============================================================================== ## Arquivos — Download de XML, PDF, log e certificado. Nenhuma consome cota. ============================================================================== ### GET /v1/jobs/{id}/arquivos Listar TODOS os arquivos de uma emissão **Descobre o que existe.** Devolve a lista de todos os arquivos gerados por um job — XML, PDF, log — cada um já com uma **URL assinada** pronta para download, o tamanho e o tipo. Use quando você **não sabe** quais arquivos aquele job produziu. Se você já sabe o tipo que quer, vá direto: | Rota | Devolve | |---|---| | `GET /v1/jobs/{id}/arquivos` | esta — a **lista** de tudo, com URLs | | `GET /v1/jobs/{id}/download/{tipo}` | **JSON com a URL** de um tipo | | `GET /v1/jobs/{id}/file/{tipo}` | **o arquivo em si** (binário) | > **As URLs expiram.** Elas valem cerca de 1 hora. Não guarde no seu banco nem mande por e-mail: gere uma nova quando precisar. URL assinada salva em algum lugar vira vazamento com prazo de validade. Vale para qualquer tipo de documento (NF-e, NFC-e, NFS-e). Não consome cota. **Respostas:** `200` lista (pode vir vazia se o job ainda não gerou arquivo) · `404` job não encontrado nesta conta · `500` erro interno. **Parâmetros (path)** - `id` (string, OBRIGATÓRIO) — `job_id` devolvido na emissão. Só encontra jobs da **sua** conta — id de outro tenant responde `404`, sem revelar que existe. Ex.: "6f8dc295-...". **Respostas:** 200 ### GET /v1/jobs/{id}/download/{tipo} Obter a URL de download de um arquivo (devolve JSON) **Devolve o endereço, não o arquivo.** A resposta é um JSON com uma **URL assinada**, o tipo, o content-type e o tamanho. Use quando quiser **entregar o link para outra pessoa ou outro sistema** — mostrar um botão de download na sua tela, mandar para um fluxo do n8n, guardar temporariamente para um processamento posterior. Se o que você quer é **baixar o conteúdo agora**, use `GET /v1/jobs/{id}/file/{tipo}`, que devolve o binário direto e evita a segunda requisição. > **A URL expira em cerca de 1 hora.** Gere na hora de usar; não persista. Não consome cota. **Respostas:** `200` com a URL · `400` tipo inválido · `404` job ou arquivo daquele tipo não encontrado · `500` erro interno. **Parâmetros (path)** - `id` (string, OBRIGATÓRIO) — `job_id` devolvido na emissão. Só encontra jobs da **sua** conta — id de outro tenant responde `404`, sem revelar que existe. Ex.: "6f8dc295-...". - `tipo` (enum: "xml" | "xml_cancelamento" | "pdf" | "log", OBRIGATÓRIO) — Tipo do arquivo: `xml` (o documento autorizado), `xml_cancelamento` (o evento de cancelamento), `pdf` (DANFE, cupom ou DANFSe) ou `log` (retorno técnico do processamento). **Respostas:** 200 ### GET /v1/jobs/{id}/file/{tipo} Baixar o arquivo (devolve o binário) **Devolve o arquivo em si**, com `Content-Disposition: attachment` — no navegador, baixa; no `curl`, use `-o`; no n8n, o bloco HTTP Request devolve o conteúdo direto. É o caminho mais curto quando você quer **guardar o XML** na sua base ou anexar o PDF a um e-mail. Uma requisição, sem passar por URL assinada. O nome do arquivo sai como `{job_id}.{extensão}` — e `log` baixa como `.json`. Existem rotas equivalentes por tipo de documento, que fazem exatamente o mesmo: `GET /v1/nfe/{id}/file/{tipo}`, `/v1/nfce/...`, `/v1/nfse/...`. Esta serve para qualquer tipo, quando você tem só o `job_id` e não sabe (ou não quer saber) qual documento é. > **Guarde o XML.** A obrigação de guarda recai sobre o **XML autorizado** — o PDF pode ser regerado a partir dele, o contrário não. A retenção aqui depende do plano; o arquivo definitivo é o seu. Não consome cota. **Respostas:** `200` com o binário · `400` tipo inválido · `404` job ou arquivo não encontrado · `500` erro interno. **Parâmetros (path)** - `id` (string, OBRIGATÓRIO) — `job_id` devolvido na emissão. Só encontra jobs da **sua** conta — id de outro tenant responde `404`, sem revelar que existe. Ex.: "6f8dc295-...". - `tipo` (enum: "xml" | "xml_cancelamento" | "pdf" | "log", OBRIGATÓRIO) — Tipo do arquivo: `xml` (o documento autorizado), `xml_cancelamento` (o evento de cancelamento), `pdf` (DANFE, cupom ou DANFSe) ou `log` (retorno técnico do processamento). **Respostas:** 200 ============================================================================== ## Captura de notas recebidas — Captura de notas recebidas por empresa ============================================================================== ### GET /v1/captura Estado da captura de uma empresa Diz, por tipo (NF-e e NFS-e), se a captura está incluída no plano, ligada e em que situação — e qual webhook está configurado. | `situacao` | Significa | |---|---| | `nao_incluida_no_plano` | O plano da conta não inclui este tipo. | | `desligada` | Incluída no plano, mas desligada nesta empresa. | | `sincronizando` | Baixando o histórico, logo depois de ligar. | | `em_dia` | Histórico baixado; consultas periódicas por notas novas. | | `aguardando_limite_de_consumo` | Espera imposta pela fonte (ex.: rejeição 656 da SEFAZ). Não é defeito: a captura retoma sozinha. | | `com_erro` | O campo `erro` diz o motivo — por exemplo, certificado vencido. | Resposta: ```json { "cpf_cnpj": "11222333000181", "nfe": { "incluida_no_plano": true, "ativa": true, "situacao": "em_dia", "ativada_em": "2026-09-25T18:22:00.000Z", "desativada_em": null, "sincronizada_em": "2026-09-25T18:23:00.000Z", "ultima_consulta": "2026-09-25T20:31:00.000Z", "proxima_consulta": "2026-09-25T21:33:00.000Z", "erro": null, "aceite_ciencia_da_operacao": { "aceito_em": "2026-09-25T18:22:00.000Z" } }, "nfse": { "incluida_no_plano": true, "ativa": true, "situacao": "sincronizando", "ativada_em": "2026-09-25T19:20:00.000Z", "desativada_em": null, "sincronizada_em": null, "ultima_consulta": "2026-09-25T19:22:00.000Z", "proxima_consulta": "2026-09-25T19:27:00.000Z", "erro": null }, "webhook": { "url": "https://seu-sistema.exemplo.com.br/conota", "ativo": true, "atualizado_em": "2026-09-25T19:40:00.000Z", "chave_de_assinatura_criada_em": "2026-09-25T19:40:00.000Z" } } ``` **Parâmetros (query)** - `cpf_cnpj` (string, máx 18 chars, OBRIGATÓRIO) — CNPJ ou CPF da empresa emissora, com ou sem máscara **Respostas:** 200 ### PUT /v1/captura Ligar ou desligar a captura Liga ou desliga, por tipo, a captura das notas que fornecedores emitem **contra** a empresa. Envie `nfe` e/ou `nfse` com `true` (ligar) ou `false` (desligar). - Exige chave de **produção**: a captura lê as notas reais, não existe captura em homologação (**403**). - Ligar exige que o plano inclua o tipo (**403**) e que a empresa tenha certificado A1 ativo e válido (**422**). - **Ligar a NF-e exige `aceite_ciencia_da_operacao: true`** (**422** sem ele). Com o aceite, a captura fica autorizada a registrar na SEFAZ, em nome da empresa, a Ciência da Operação (evento 210210) de cada NF-e recebida — é o que libera o XML completo. A Ciência só informa que a empresa tomou conhecimento da nota; não confirma a operação. O registro automático entra em operação numa próxima etapa; até lá, a NF-e chega, em geral, como resumo (`xml_integral: false`). - Ao ligar, o histórico disponível na fonte é baixado primeiro (`situacao: sincronizando`). O histórico **não gera aviso** de webhook. - Desligar para de buscar notas novas. O que já foi capturado continua disponível. Responde com o estado atualizado, no mesmo formato de `GET /v1/captura`. **Corpo (JSON)** — obrigatório - `cpf_cnpj` (string, máx 18 chars, OBRIGATÓRIO) — CNPJ ou CPF da empresa emissora, com ou sem máscara - `nfe` (boolean) - `nfse` (boolean) - `aceite_ciencia_da_operacao` (boolean) **Respostas:** 200 ### PUT /v1/captura/webhook Configurar o webhook da empresa Um webhook por empresa (CNPJ). A `url` precisa estar nos **destinos de webhook autorizados de produção** da conta (console, Integração > Destinos de webhook) — senão, **422**. `ativo: false` desliga. Na primeira configuração da conta, a resposta traz `chave_de_assinatura.valor` — **a única vez que ela aparece**. Guarde-a: ela assina os avisos de todas as empresas da conta. **Eventos**: `nota_recebida` (nota recebida nova) e `nota_recebida_cancelada`. O histórico baixado ao ligar a captura não gera aviso, nem as notas que a própria empresa emitiu. **Headers de cada aviso**: `X-Conota-Evento`, `X-Conota-Entrega` (id único — use para descartar repetição), `X-Conota-Tentativa`, `X-Conota-Timestamp` e `X-Conota-Signature`. **Como conferir a assinatura**: 1. Leia o corpo **bruto**, antes de interpretar o JSON. 2. Recuse se `X-Conota-Timestamp` estiver a mais de 5 minutos do seu relógio. 3. Calcule `"sha256=" + hex(HMAC-SHA256(chave, "."))`. 4. Compare com `X-Conota-Signature` em tempo constante. **Entrega**: responda 2xx. Sem 2xx, tentamos de novo em 1 min, 5 min, 15 min, 1 h, 3 h, 6 h e 12 h (8 tentativas). Redirecionamento não é seguido. Corpo de um aviso: ```json { "evento": "nota_recebida", "entrega_id": "5b0f3c1e-0000-4000-8000-000000000000", "ocorrido_em": "2026-09-25T19:22:10.000Z", "empresa": { "cpf_cnpj": "11222333000181" }, "nota": { "tipo": "nfse", "chave": "35503081299888777000166000000000012326081234567895", "papel": "tomador", "numero": "123", "emissor": { "cpf_cnpj": "99888777000166", "nome": "PRESTADORA EXEMPLO LTDA" }, "valor": 1500, "data_emissao": "2026-09-20T13:00:00.000Z", "situacao": "autorizada", "municipio_incidencia": "3550308", "xml_integral": true }, "xml_url": "https://api.conota.dev/v1/captura/notas/xml?chave=35503081299888777000166000000000012326081234567895&cpf_cnpj=11222333000181" } ``` **Corpo (JSON)** — obrigatório - `cpf_cnpj` (string, máx 18 chars, OBRIGATÓRIO) — CNPJ ou CPF da empresa emissora, com ou sem máscara - `url` (string, máx 2048 chars) - `ativo` (boolean, default true) **Respostas:** 200 ### POST /v1/captura/webhook/segredo Gerar ou trocar a chave de assinatura Gera uma chave de assinatura nova para a conta e a devolve **uma única vez**. A troca vale na hora: os avisos seguintes já saem assinados com a nova, para todas as empresas da conta. Sem corpo. Exige chave de produção. **Respostas:** 200 ### GET /v1/captura/notas Listar notas recebidas Lista as notas em que a empresa figura como **tomadora, destinatária ou intermediária**. As notas que a própria empresa emitiu não aparecem aqui. - `desde` e `ate` são datas de emissão, no fuso de Brasília. - `situacao` vem como a fonte informou. Um código que ainda não interpretamos aparece cru (ex.: `cstat-107`), nunca como `autorizada`. - Numa NF-e, `xml_integral: false` significa que só o resumo chegou até agora. Resposta: ```json { "cpf_cnpj": "11222333000181", "pagina": 1, "limite": 50, "total": 1, "notas": [ { "tipo": "nfse", "chave": "35503081299888777000166000000000012326081234567895", "papel": "tomador", "numero": "123", "emissor": { "cpf_cnpj": "99888777000166", "nome": "PRESTADORA EXEMPLO LTDA" }, "valor": 1500, "data_emissao": "2026-09-20T13:00:00.000Z", "situacao": "autorizada", "municipio_incidencia": "3550308", "xml_integral": true, "capturada_em": "2026-09-25T19:22:10.000Z" } ] } ``` **Parâmetros (query)** - `cpf_cnpj` (string, máx 18 chars, OBRIGATÓRIO) — CNPJ ou CPF da empresa emissora, com ou sem máscara - `tipo` (enum: "nfe" | "nfse") - `desde` (string, padrão ^\d{4}-\d{2}-\d{2}$) — AAAA-MM-DD, no fuso de Brasília - `ate` (string, padrão ^\d{4}-\d{2}-\d{2}$) — AAAA-MM-DD, no fuso de Brasília - `situacao` (string, máx 30 chars) - `pagina` (integer, default 1, min 1) - `limite` (integer, default 50, min 1) **Respostas:** 200 ### GET /v1/captura/notas/xml Baixar o XML de uma nota recebida Devolve o XML (`application/xml`). O header `X-Conota-Documento` diz se é a nota `integral` ou só o `resumo` (NF-e cuja nota integral ainda não chegou). Informe `cpf_cnpj` quando a mesma nota aparece em mais de uma empresa da conta — sem ele, a resposta é **409**. **Parâmetros (query)** - `chave` (string, padrão ^(\d{44}|\d{50})$, OBRIGATÓRIO) — Chave de acesso: 44 dígitos (NF-e) ou 50 (NFS-e) - `cpf_cnpj` (string, máx 18 chars) — CNPJ ou CPF da empresa emissora, com ou sem máscara **Respostas:** 200 ============================================================================== ## ISSQN ============================================================================== ### GET /v1/issqn/aliquota Consultar a aliquota de ISSQN de um servico num municipio Devolve a **alíquota de ISSQN** vigente para um município e um código de serviço, na data informada. Os dados vêm do extrato oficial da NFS-e Nacional. > **A cobertura não é total.** O extrato traz 5.341 dos 5.571 municípios. Faltam 230, entre eles 11 capitais — São Paulo, Rio de Janeiro, Belo Horizonte, Brasília, Fortaleza, Recife, Manaus, Belém, Maceió, Campo Grande e Teresina. Esses municípios mantêm sistema próprio e não alimentam o cadastro nacional; para eles a resposta é `municipio_fora_do_extrato`. **`aliquota: null` não é alíquota zero.** Significa que o município não publicou o valor. A menor alíquota que existe no arquivo é 2 (o piso da LC 116) e o valor zero não aparece nenhuma vez. Quando não há resposta, o campo `motivo` diz qual das quatro situações ocorreu: `municipio_fora_do_extrato`, `servico_nao_consta`, `sem_vigencia_na_data` ou `aliquota_nao_informada`. **Parâmetros (query)** - `codigo_ibge` (string, OBRIGATÓRIO) — Código IBGE do município, 7 dígitos. Ex.: 4106902. - `codigo_servico` (string, OBRIGATÓRIO) — Código de serviço da NFS-e Nacional. Ex.: 01.01.01.000. - `data` (string) — Data da prestação (AAAA-MM-DD). Padrão: hoje. **Respostas:** 200 ### GET /v1/issqn/municipios/{codigo_ibge} Listar as aliquotas vigentes de um municipio Todas as alíquotas vigentes de um município, para montar um seletor de serviços ou conferir o cadastro de uma vez só. Serviços cuja alíquota o município não publicou vêm com `aliquota: null` — e continuam na lista de propósito: saber que o serviço existe sem alíquota publicada é diferente de não saber nada. **Parâmetros (path)** - `codigo_ibge` (string, OBRIGATÓRIO) — Código IBGE, 7 dígitos. **Respostas:** 200 ### GET /v1/issqn/extracao De quando e o dado de aliquotas Data de referência do extrato carregado e o tamanho da cobertura. Existe porque "de quando é essa alíquota?" é a primeira pergunta de quem for contestar um cálculo — e sem esta rota não haveria resposta. **Respostas:** 200 ============================================================================== ## Catálogo ============================================================================== ### POST /v1/catalogos Criar um catálogo para a empresa O catálogo pertence à **empresa emissora**. Se você emite por várias empresas, cada uma tem o seu, isolado das demais. Cada empresa tem um catálogo: criar um segundo responde `409`. Para recomeçar, desative o atual e crie outro. Guarde o **`catalogo_id`** da resposta — é por ele que todas as demais rotas do catálogo são chamadas, e você não precisa repetir o CNPJ depois. **Respostas:** `201` criado · `404` empresa não encontrada nesta conta · `409` a empresa já tem catálogo ativo · `500` erro interno. **Corpo (JSON)** — obrigatório - `cpf_cnpj` (string, OBRIGATÓRIO) — CNPJ da empresa emissora, com ou sem máscara. CNPJ alfanumérico é aceito. - `nome` (string, máx 120 chars, OBRIGATÓRIO) — Como você chama este catálogo. Ex.: `Padrão`, `Loja física`. - `descricao` (string, máx 500 chars) **Exemplo** ```json { "cpf_cnpj": "11222333000181", "nome": "Padrão", "descricao": "Produtos e serviços da operação principal" } ``` **Respostas:** 200 ### GET /v1/catalogos Listar os catálogos de uma empresa Devolve os catálogos da empresa, com a contagem de itens de cada um. Por padrão só os ativos — use `incluir_inativos` para ver também os desativados. **Parâmetros (query)** - `cpf_cnpj` (string, OBRIGATÓRIO) — CNPJ da empresa emissora. - `incluir_inativos` (boolean, default false) — Inclui também os catálogos desativados, para encontrar um e reativá-lo. **Respostas:** 200 ### GET /v1/catalogos/{id} Ver um catálogo Use o `catalogo_id` devolvido na criação. A empresa já está vinculada a ele, então não é preciso informar o CNPJ de novo. **Parâmetros (path)** - `id` (string, padrão ^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$, OBRIGATÓRIO) — O `catalogo_id` devolvido na criacao. **Respostas:** 200 ### PUT /v1/catalogos/{id} Renomear ou redescrever um catálogo **Parâmetros (path)** - `id` (string, padrão ^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$, OBRIGATÓRIO) — O `catalogo_id` devolvido na criacao. **Corpo (JSON)** — obrigatório - `nome` (string, máx 120 chars) - `descricao` (string, máx 500 chars) **Respostas:** 200 ### DELETE /v1/catalogos/{id} Desativar um catálogo O catálogo sai de uso, e com ele os itens. Nada é apagado: os itens continuam lá e voltam inteiros se você reativar o catálogo. Desativar libera a empresa para criar um novo catálogo — ou para reativar outro. **Parâmetros (path)** - `id` (string, padrão ^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$, OBRIGATÓRIO) — O `catalogo_id` devolvido na criacao. **Respostas:** 200 ### POST /v1/catalogos/{id}/reativar Reativar um catálogo desativado Traz o catálogo de volta, com todos os itens e exceções como estavam. Nada precisa ser recadastrado. Como cada empresa tem um catálogo ativo, reativar só funciona se não houver outro ativo na mesma empresa — nesse caso, desative o outro antes. Chamar num catálogo que já está ativo não é erro: responde `200` e nada muda. **Respostas:** `200` reativado · `404` catálogo não encontrado · `409` a empresa já tem outro catálogo ativo · `500` erro interno. **Parâmetros (path)** - `id` (string, padrão ^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$, OBRIGATÓRIO) — O `catalogo_id` devolvido na criacao. **Respostas:** 200 ### GET /v1/catalogos/{id}/produtos Listar produtos do catálogo Lista paginada. Informe `sku` para filtrar por um item exato — a resposta é a mesma lista, com `total` 1 ou 0. `busca` procura texto livre em `sku`, `codigo_interno`, `nome` e `descricao`. **Parâmetros (path)** - `id` (string, padrão ^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$, OBRIGATÓRIO) — O `catalogo_id` devolvido na criacao. **Parâmetros (query)** - `sku` (string, máx 60 chars) — Filtra pelo `sku` exato. - `busca` (string) — Texto livre. - `incluir_removidos` (boolean, default false) — Inclui também os removidos, para encontrar um e reativá-lo. - `pagina` (integer, default 1, min 1) - `limite` (integer, default 50, min 1) **Respostas:** 200 ### POST /v1/catalogos/{id}/produtos Cadastrar produto O `sku` é o código **seu** para o item, e é por ele que o item é referenciado nas demais rotas. `codigo_interno` é um segundo código livre, para o seu uso — não vai para a nota. **Respostas:** `201` criado · `400` corpo inválido ou contradição fiscal no bloco `fiscal` · `404` catálogo não encontrado · `409` já existe produto com este código · `500` erro interno. **Parâmetros (path)** - `id` (string, padrão ^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$, OBRIGATÓRIO) — O `catalogo_id` devolvido na criacao. **Corpo (JSON)** — obrigatório - `sku` (string, máx 60 chars, padrão ^\S(.*\S)?$, OBRIGATÓRIO) — Seu código para o item. Barra, espaço e acento são aceitos — é o seu código, não o nosso. Só não pode começar ou terminar com espaço. - `codigo_interno` (string, máx 60 chars) — Seu código interno. Opcional. - `nome` (string, máx 200 chars, OBRIGATÓRIO) - `descricao` (string, máx 1000 chars) - `unidade` (string, máx 10 chars) - `valor_unitario` (number, min 0) — Preço padrão, usado só quando a emissão não informar o valor. Omita para deixar sem preço — `0` significa preço zero, não “não definido”. - `fiscal` (object) — Bloco fiscal do item, no mesmo formato que a emissão aceita — espelhar a estrutura é o que permite aplicá-lo sem tradução. Os valores são os que **você** declara: o Conota guarda e aplica, não sugere nem corrige. A única coisa recusada é contradição interna: um CST de tributação de IBS/CBS com todas as alíquotas em zero. Aqui também moram `NCM`, `CEST` e `cEAN`, que não são tributo mas são do item. **Exemplo** ```json { "sku": "BAL-001-MET", "codigo_interno": "INT-4471", "nome": "Balão Metalizado", "unidade": "UN", "valor_unitario": 20, "fiscal": { "icms": { "CST": "41", "orig": 0 }, "pis": { "CST": "07" }, "cofins": { "CST": "07" }, "ibscbs": { "cst": "000", "classificacao": "000001", "ibs_uf": { "aliquota": 0.1 }, "ibs_mun": { "aliquota": 0 }, "cbs": { "aliquota": 0.9 } } } } ``` **Respostas:** 200 ### PUT /v1/catalogos/{id}/produtos Atualizar produto **Substitui o item inteiro.** Campos omitidos ficam vazios, e `fiscal` é trocado por completo — não é mesclado com o que estava lá. Para alterar um campo só: liste o produto com `?sku=`, altere o que precisa e devolva o objeto completo aqui. **Parâmetros (path)** - `id` (string, padrão ^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$, OBRIGATÓRIO) — O `catalogo_id` devolvido na criacao. **Parâmetros (query)** - `sku` (string, máx 60 chars, OBRIGATÓRIO) — O `sku` do item, exatamente como cadastrado. **Corpo (JSON)** — obrigatório - `codigo_interno` (string, máx 60 chars) - `nome` (string, máx 200 chars, OBRIGATÓRIO) - `descricao` (string, máx 1000 chars) - `unidade` (string, máx 10 chars) - `valor_unitario` (number, min 0) — Preço padrão, usado só quando a emissão não informar o valor. Omita para deixar sem preço — `0` significa preço zero, não “não definido”. - `fiscal` (object) — Bloco fiscal do item, no mesmo formato que a emissão aceita — espelhar a estrutura é o que permite aplicá-lo sem tradução. Os valores são os que **você** declara: o Conota guarda e aplica, não sugere nem corrige. A única coisa recusada é contradição interna: um CST de tributação de IBS/CBS com todas as alíquotas em zero. Aqui também moram `NCM`, `CEST` e `cEAN`, que não são tributo mas são do item. **Respostas:** 200 ### DELETE /v1/catalogos/{id}/produtos Remover produto O produto sai das listagens e o código fica livre para ser cadastrado de novo. Nada é apagado — as exceções continuam guardadas e voltam inteiras se você reativar. Cadastrar o mesmo código de novo **não** é a mesma coisa que reativar: o cadastro novo nasce sem exceção nenhuma. **Parâmetros (path)** - `id` (string, padrão ^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$, OBRIGATÓRIO) — O `catalogo_id` devolvido na criacao. **Parâmetros (query)** - `sku` (string, máx 60 chars, OBRIGATÓRIO) — O `sku` do item, exatamente como cadastrado. **Respostas:** 200 ### POST /v1/catalogos/{id}/produtos/reativar Reativar produto removido Traz o produto de volta **com as exceções que ele tinha**. Isso é o que diferencia reativar de cadastrar de novo: o cadastro novo nasce sem nenhuma exceção, e para um item com uma dezena de municípios isso significa refazer todos. Só funciona se não houver outro produto ativo com o mesmo código — nesse caso, remova o outro antes. Chamar num produto que já está ativo não é erro: responde `200` e nada muda. **Respostas:** `200` reativado · `404` catálogo ou item não encontrado · `409` já existe outro ativo com este código · `500` erro interno. **Parâmetros (path)** - `id` (string, padrão ^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$, OBRIGATÓRIO) — O `catalogo_id` devolvido na criacao. **Parâmetros (query)** - `sku` (string, máx 60 chars, OBRIGATÓRIO) — O `sku` do item, exatamente como cadastrado. **Respostas:** 200 ### GET /v1/catalogos/{id}/produtos/excecoes Listar as exceções de produto **Parâmetros (path)** - `id` (string, padrão ^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$, OBRIGATÓRIO) — O `catalogo_id` devolvido na criacao. **Parâmetros (query)** - `sku` (string, máx 60 chars, OBRIGATÓRIO) — O `sku` do item, exatamente como cadastrado. **Respostas:** 200 ### PUT /v1/catalogos/{id}/produtos/excecoes Definir exceção de produto Define o bloco fiscal que vale quando a condição bater — por exemplo, um município que exige códigos diferentes dos que você usa no resto do país. Chamar de novo na mesma condição substitui a exceção anterior. A condição é o próprio endereço, então não há identificador a guardar. > **A exceção substitui o bloco `fiscal` inteiro** — não é mesclada com o padrão do item. Para uma cidade onde só falta um campo, liste o produto com `?sku=`, acrescente o campo e mande o bloco completo aqui. Condições aceitas: `municipio` (código IBGE, 7 dígitos). **Respostas:** `200` definida · `400` condição não suportada, bloco vazio ou contradição fiscal · `404` catálogo ou produto não encontrado · `500` erro interno. **Parâmetros (path)** - `id` (string, padrão ^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$, OBRIGATÓRIO) — O `catalogo_id` devolvido na criacao. **Parâmetros (query)** - `sku` (string, máx 60 chars, OBRIGATÓRIO) — O `sku` do item, exatamente como cadastrado. - `tipo` (string, OBRIGATÓRIO) — Condição da exceção. Aceita: `municipio`. - `valor` (string, OBRIGATÓRIO) — Para `municipio`: o código IBGE com 7 dígitos. **Corpo (JSON)** — obrigatório - `fiscal` (object, OBRIGATÓRIO) — Bloco fiscal do item, no mesmo formato que a emissão aceita — espelhar a estrutura é o que permite aplicá-lo sem tradução. Os valores são os que **você** declara: o Conota guarda e aplica, não sugere nem corrige. A única coisa recusada é contradição interna: um CST de tributação de IBS/CBS com todas as alíquotas em zero. Aqui também moram `NCM`, `CEST` e `cEAN`, que não são tributo mas são do item. **Respostas:** 200 ### DELETE /v1/catalogos/{id}/produtos/excecoes Apagar exceção de produto Apagada a exceção, aquela condição volta a usar o bloco padrão do item. **Parâmetros (path)** - `id` (string, padrão ^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$, OBRIGATÓRIO) — O `catalogo_id` devolvido na criacao. **Parâmetros (query)** - `sku` (string, máx 60 chars, OBRIGATÓRIO) — O `sku` do item, exatamente como cadastrado. - `tipo` (string, OBRIGATÓRIO) - `valor` (string, OBRIGATÓRIO) **Respostas:** 200 ### GET /v1/catalogos/{id}/produtos/resolver Resolver o bloco fiscal de produto para uma condição Diz qual bloco `fiscal` se aplica, e **por quê**: `aplicado` vem `excecao` quando houver uma para a condição informada, e `padrao` quando não houver. Isto não é recomendação fiscal — é o seu próprio cadastro, resolvido. **Parâmetros (path)** - `id` (string, padrão ^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$, OBRIGATÓRIO) — O `catalogo_id` devolvido na criacao. **Parâmetros (query)** - `sku` (string, máx 60 chars, OBRIGATÓRIO) — O `sku` do item, exatamente como cadastrado. - `municipio` (string) — Código IBGE do município, 7 dígitos. **Respostas:** 200 ### GET /v1/catalogos/{id}/servicos Listar servicos do catálogo Lista paginada. Informe `codigo` para filtrar por um item exato — a resposta é a mesma lista, com `total` 1 ou 0. `busca` procura texto livre em `codigo`, `codigo_interno`, `nome` e `descricao`. **Parâmetros (path)** - `id` (string, padrão ^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$, OBRIGATÓRIO) — O `catalogo_id` devolvido na criacao. **Parâmetros (query)** - `codigo` (string, máx 60 chars) — Filtra pelo `codigo` exato. - `busca` (string) — Texto livre. - `incluir_removidos` (boolean, default false) — Inclui também os removidos, para encontrar um e reativá-lo. - `pagina` (integer, default 1, min 1) - `limite` (integer, default 50, min 1) **Respostas:** 200 ### POST /v1/catalogos/{id}/servicos Cadastrar servico O `codigo` é o código **seu** para o item, e é por ele que o item é referenciado nas demais rotas. `codigo_interno` é um segundo código livre, para o seu uso — não vai para a nota. **Respostas:** `201` criado · `400` corpo inválido ou contradição fiscal no bloco `fiscal` · `404` catálogo não encontrado · `409` já existe servico com este código · `500` erro interno. **Parâmetros (path)** - `id` (string, padrão ^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$, OBRIGATÓRIO) — O `catalogo_id` devolvido na criacao. **Corpo (JSON)** — obrigatório - `codigo` (string, máx 60 chars, padrão ^\S(.*\S)?$, OBRIGATÓRIO) — Seu código para o item. Barra, espaço e acento são aceitos — é o seu código, não o nosso. Só não pode começar ou terminar com espaço. - `codigo_interno` (string, máx 60 chars) — Seu código interno. Opcional. - `nome` (string, máx 200 chars, OBRIGATÓRIO) - `descricao` (string, máx 1000 chars) - `unidade` (string, máx 10 chars) - `valor_unitario` (number, min 0) — Preço padrão, usado só quando a emissão não informar o valor. Omita para deixar sem preço — `0` significa preço zero, não “não definido”. - `fiscal` (object) — Bloco fiscal do item, no mesmo formato que a emissão aceita — espelhar a estrutura é o que permite aplicá-lo sem tradução. Os valores são os que **você** declara: o Conota guarda e aplica, não sugere nem corrige. Cada chave é um nó do **DPS**: `cServ` (o que está em `serv.cServ`), `tribMun` (de `valores.trib.tribMun`) e `IBSCBS`. Não entram aqui o município de prestação nem os valores — eles mudam a cada nota. Para um município que exija códigos diferentes, use uma **exceção**. **Exemplo** ```json { "codigo": "RECREACAO", "codigo_interno": "SRV-12", "nome": "Recreação e animação infantil", "unidade": "UN", "fiscal": { "cServ": { "cTribNac": "121701", "cNBS": "125080000" }, "tribMun": { "tribISSQN": 1, "tpRetISSQN": 1 }, "IBSCBS": { "finNFSe": 0, "indFinal": 1, "cIndOp": "030104", "indDest": 0, "valores": { "trib": { "gIBSCBS": { "CST": "000", "cClassTrib": "000001" } } } } } } ``` **Respostas:** 200 ### PUT /v1/catalogos/{id}/servicos Atualizar servico **Substitui o item inteiro.** Campos omitidos ficam vazios, e `fiscal` é trocado por completo — não é mesclado com o que estava lá. Para alterar um campo só: liste o servico com `?codigo=`, altere o que precisa e devolva o objeto completo aqui. **Parâmetros (path)** - `id` (string, padrão ^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$, OBRIGATÓRIO) — O `catalogo_id` devolvido na criacao. **Parâmetros (query)** - `codigo` (string, máx 60 chars, OBRIGATÓRIO) — O `codigo` do item, exatamente como cadastrado. **Corpo (JSON)** — obrigatório - `codigo_interno` (string, máx 60 chars) - `nome` (string, máx 200 chars, OBRIGATÓRIO) - `descricao` (string, máx 1000 chars) - `unidade` (string, máx 10 chars) - `valor_unitario` (number, min 0) — Preço padrão, usado só quando a emissão não informar o valor. Omita para deixar sem preço — `0` significa preço zero, não “não definido”. - `fiscal` (object) — Bloco fiscal do item, no mesmo formato que a emissão aceita — espelhar a estrutura é o que permite aplicá-lo sem tradução. Os valores são os que **você** declara: o Conota guarda e aplica, não sugere nem corrige. Cada chave é um nó do **DPS**: `cServ` (o que está em `serv.cServ`), `tribMun` (de `valores.trib.tribMun`) e `IBSCBS`. Não entram aqui o município de prestação nem os valores — eles mudam a cada nota. Para um município que exija códigos diferentes, use uma **exceção**. **Respostas:** 200 ### DELETE /v1/catalogos/{id}/servicos Remover servico O servico sai das listagens e o código fica livre para ser cadastrado de novo. Nada é apagado — as exceções continuam guardadas e voltam inteiras se você reativar. Cadastrar o mesmo código de novo **não** é a mesma coisa que reativar: o cadastro novo nasce sem exceção nenhuma. **Parâmetros (path)** - `id` (string, padrão ^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$, OBRIGATÓRIO) — O `catalogo_id` devolvido na criacao. **Parâmetros (query)** - `codigo` (string, máx 60 chars, OBRIGATÓRIO) — O `codigo` do item, exatamente como cadastrado. **Respostas:** 200 ### POST /v1/catalogos/{id}/servicos/reativar Reativar servico removido Traz o servico de volta **com as exceções que ele tinha**. Isso é o que diferencia reativar de cadastrar de novo: o cadastro novo nasce sem nenhuma exceção, e para um item com uma dezena de municípios isso significa refazer todos. Só funciona se não houver outro servico ativo com o mesmo código — nesse caso, remova o outro antes. Chamar num servico que já está ativo não é erro: responde `200` e nada muda. **Respostas:** `200` reativado · `404` catálogo ou item não encontrado · `409` já existe outro ativo com este código · `500` erro interno. **Parâmetros (path)** - `id` (string, padrão ^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$, OBRIGATÓRIO) — O `catalogo_id` devolvido na criacao. **Parâmetros (query)** - `codigo` (string, máx 60 chars, OBRIGATÓRIO) — O `codigo` do item, exatamente como cadastrado. **Respostas:** 200 ### GET /v1/catalogos/{id}/servicos/excecoes Listar as exceções de servico **Parâmetros (path)** - `id` (string, padrão ^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$, OBRIGATÓRIO) — O `catalogo_id` devolvido na criacao. **Parâmetros (query)** - `codigo` (string, máx 60 chars, OBRIGATÓRIO) — O `codigo` do item, exatamente como cadastrado. **Respostas:** 200 ### PUT /v1/catalogos/{id}/servicos/excecoes Definir exceção de servico Define o bloco fiscal que vale quando a condição bater — por exemplo, um município que exige códigos diferentes dos que você usa no resto do país. Chamar de novo na mesma condição substitui a exceção anterior. A condição é o próprio endereço, então não há identificador a guardar. > **A exceção substitui o bloco `fiscal` inteiro** — não é mesclada com o padrão do item. Para uma cidade onde só falta um campo, liste o servico com `?codigo=`, acrescente o campo e mande o bloco completo aqui. Condições aceitas: `municipio` (código IBGE, 7 dígitos). **Respostas:** `200` definida · `400` condição não suportada, bloco vazio ou contradição fiscal · `404` catálogo ou servico não encontrado · `500` erro interno. **Parâmetros (path)** - `id` (string, padrão ^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$, OBRIGATÓRIO) — O `catalogo_id` devolvido na criacao. **Parâmetros (query)** - `codigo` (string, máx 60 chars, OBRIGATÓRIO) — O `codigo` do item, exatamente como cadastrado. - `tipo` (string, OBRIGATÓRIO) — Condição da exceção. Aceita: `municipio`. - `valor` (string, OBRIGATÓRIO) — Para `municipio`: o código IBGE com 7 dígitos. **Corpo (JSON)** — obrigatório - `fiscal` (object, OBRIGATÓRIO) — Bloco fiscal do item, no mesmo formato que a emissão aceita — espelhar a estrutura é o que permite aplicá-lo sem tradução. Os valores são os que **você** declara: o Conota guarda e aplica, não sugere nem corrige. Cada chave é um nó do **DPS**: `cServ` (o que está em `serv.cServ`), `tribMun` (de `valores.trib.tribMun`) e `IBSCBS`. Não entram aqui o município de prestação nem os valores — eles mudam a cada nota. Para um município que exija códigos diferentes, use uma **exceção**. **Respostas:** 200 ### DELETE /v1/catalogos/{id}/servicos/excecoes Apagar exceção de servico Apagada a exceção, aquela condição volta a usar o bloco padrão do item. **Parâmetros (path)** - `id` (string, padrão ^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$, OBRIGATÓRIO) — O `catalogo_id` devolvido na criacao. **Parâmetros (query)** - `codigo` (string, máx 60 chars, OBRIGATÓRIO) — O `codigo` do item, exatamente como cadastrado. - `tipo` (string, OBRIGATÓRIO) - `valor` (string, OBRIGATÓRIO) **Respostas:** 200 ### GET /v1/catalogos/{id}/servicos/resolver Resolver o bloco fiscal de servico para uma condição Diz qual bloco `fiscal` se aplica, e **por quê**: `aplicado` vem `excecao` quando houver uma para a condição informada, e `padrao` quando não houver. Isto não é recomendação fiscal — é o seu próprio cadastro, resolvido. **Parâmetros (path)** - `id` (string, padrão ^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$, OBRIGATÓRIO) — O `catalogo_id` devolvido na criacao. **Parâmetros (query)** - `codigo` (string, máx 60 chars, OBRIGATÓRIO) — O `codigo` do item, exatamente como cadastrado. - `municipio` (string) — Código IBGE do município, 7 dígitos. **Respostas:** 200