API de emissão do EasyNotas
Uma API REST para o seu sistema emitir, consultar e cancelar notas fiscais, inutilizar numeração de NF-e e ler as notas que fornecedores emitiram contra os seus clientes. O EasyNotas cuida da comunicação com a SEFAZ e com as prefeituras.
Novidades de setembro/2026
Tudo que o painel do EasyNotas ganhou desde julho agora também funciona pela API. Integrações que já existem continuam funcionando sem mudança: todos os campos novos são opcionais.
Reforma Tributária na NFS-e
NBS, indicador de operação e CST/classificação de IBS/CBS por nota. Sem eles, vale o padrão cadastrado na empresa.
Retenções na NFS-e
ISS retido, IR, PIS, COFINS e CSLL. IRRF de 1,5% automático para tomador PJ nos serviços de intermediação.
Mais campos na NFS-e
Informações complementares, município da prestação e tomador não identificado (consumidor final).
NF-e mais completa
Série própria da empresa, devolução com nota referenciada por item, cBenef e PIS/COFINS por item.
Inutilização de numeração
Inutilize na SEFAZ uma faixa de números de NF-e que foram pulados.
Notas recebidas
Liste as notas emitidas contra as empresas do escritório e faça a manifestação do destinatário.
numbering:write, received:read e received:manifest. Inutilização e manifestação são eventos irreversíveis na SEFAZ: se o seu sistema não deve fazê-los, gere uma chave nova só com os escopos que usa e revogue a antiga. Os Termos de Uso da API foram atualizados (versão 2026-09-30).Visão geral
Seu sistema chama a API com os dados da nota. A API valida autenticação, pagamento do escritório, habilitação da empresa e as regras fiscais, coloca a nota na fila e responde na hora com um id. A autorização na SEFAZ ou na prefeitura acontece em seguida, e o resultado chega por webhook ou por consulta.
Os dados do emitente nunca vêm do seu sistema. Você informa só o cnpj_emitente, e regime tributário, inscrições, endereço, certificado e padrões fiscais saem do cadastro da empresa no EasyNotas. Se o cadastro estiver errado, a nota sai errada: corrija no painel, não no payload.
O que a API faz
| Recurso | Rota | Situação |
|---|---|---|
| Emitir NFS-e (Nacional ou municipal de São Paulo) | POST /v1/invoices | Em produção |
| Emitir NF-e modelo 55, inclusive devolução | POST /v1/invoices | Em produção |
| Emitir CT-e, CT-e OS, CT-e Simplificado, MDF-e, NFCom e DC-e | POST /v1/invoices | Disponível, sem emissão real pela API ainda |
| Consultar e listar notas, com PDF, XML e chave de acesso | GET /v1/invoices | Em produção |
| Cancelar e consultar a janela de cancelamento | POST /v1/invoices/{id}/cancel | Em produção |
| Webhooks de status assinados | URL cadastrada no painel | Em produção |
| Inutilizar numeração de NF-e | POST /v1/nfe/inutilizacoes | novo |
| Listar notas recebidas (Buscador XML) | GET /v1/received | novo |
| Manifestação do destinatário | POST /v1/received/{chave}/manifest | novo |
Quem usa
ERPs e sistemas de gestão que precisam emitir a nota logo depois do faturamento, plataformas que vendem serviços e querem emitir a NFS-e do cliente, e escritórios contábeis que automatizam a rotina dos clientes. A integração exige trabalho de desenvolvimento do lado de quem consome a API. O EasyNotas fornece a API, esta documentação e o suporte.
Primeiros passos
sandbox no painel/v1/health1. Gere a chave
No EasyNotas, o dono do escritório abre Configurações → API / Desenvolvedores, escolhe o ambiente, aceita os Termos de Uso da API e clica em Gerar nova API key. A chave de sandbox começa com enk_test_ e a de produção com enk_live_.
2. Teste a conexão
curl -i https://pufsgcaxubzxdtmsenjx.supabase.co/functions/v1/api-v1/v1/healthResposta 200 {"status":"ok"}. Essa rota não exige autenticação.
3. Emita a primeira nota
curl -i -X POST 'https://pufsgcaxubzxdtmsenjx.supabase.co/functions/v1/api-v1/v1/invoices' \
-H 'Authorization: Bearer enk_test_SUA_CHAVE' \
-H 'X-EasyNotas-Environment: sandbox' \
-H 'Idempotency-Key: fatura-2026-000123-teste' \
-H 'Content-Type: application/json' \
-d '{
"tipo": "nfse_nacional",
"cnpj_emitente": "12345678000190",
"tomador": {
"documento": "11222333000181",
"razao_social": "Cliente Exemplo LTDA",
"email": "financeiro@exemplo.com.br"
},
"servico": {
"discriminacao": "Consultoria mensal em tecnologia - setembro/2026",
"valor_servicos": 1500.00,
"codigo_tributacao_nacional": "010701"
}
}'A resposta é 202 Accepted com o id da nota e status: "queued". Guarde o id: é por ele que você acompanha a nota. O 202 quer dizer "aceita e na fila", não "autorizada".
4. Acompanhe o resultado
Configure a URL de webhook no mesmo painel e o EasyNotas avisa quando a nota for autorizada, rejeitada ou cancelada (veja Webhooks). Se preferir consultar, use GET /v1/invoices/{id}: enquanto a nota está em processing a resposta traz Retry-After: 5; antes disso, na fila, consultar a cada poucos minutos basta.
5. Vá para produção
Gere uma chave de produção, troque o header para production e confirme no cadastro que a empresa está habilitada para o tipo de nota e tem certificado digital válido.
Autenticação e escopos
| Header | Quando | Valor |
|---|---|---|
Authorization | Toda rota, menos /v1/health | Bearer enk_live_… ou Bearer enk_test_… |
X-EasyNotas-Environment | Toda rota, menos /v1/health | sandbox ou production. Precisa bater com o ambiente da chave, senão 403 environment_mismatch. |
Idempotency-Key | Só em POST /v1/invoices | Texto único por nota, formato ^[A-Za-z0-9_-]{16,64}$ |
Content-Type | Todo POST | application/json |
Escopos
Cada chave carrega uma lista de permissões. Uma rota chamada sem o escopo responde 403 insufficient_scope.
| Escopo | Libera |
|---|---|
invoices:write | Emitir notas |
invoices:read | Consultar e listar notas, janela de cancelamento e histórico de inutilizações |
invoices:cancel | Cancelar notas. Chaves geradas antes de 20/07/2026 não têm este escopo: gere uma nova. |
numbering:write novo | Inutilizar numeração de NF-e |
received:read novo | Listar e consultar notas recebidas, com download do XML |
received:manifest novo | Manifestação do destinatário |
Ambientes
| Ambiente | Chave | O que acontece |
|---|---|---|
sandbox | enk_test_ | Emite em homologação, sem valor fiscal e sem consumir o plano. Inutilização vai para a homologação e não entra no histórico do cliente. Manifestação é bloqueada. |
production | enk_live_ | Emite documentos fiscais reais e consome o plano do escritório. |
Idempotência, fila e limites
Idempotência
Emitir nota é irreversível. Se a rede cair no meio da chamada e você reenviar, a Idempotency-Key impede a segunda nota. Use um valor estável do seu sistema, como o número da fatura.
- Mesma chave e mesmo corpo: a API devolve a resposta original, com o header
Idempotency-Replayed: true. - Mesma chave e corpo diferente:
409 idempotency_conflict, mas só quando a chave já gerou um202. Se a primeira tentativa foi recusada (4xx ou 5xx), corrija o corpo e reenvie com a mesma chave. - A proteção vale por 30 dias. Depois disso a mesma chave pode ser aceita como uma nota nova.
- Mesma chave enquanto a primeira chamada ainda roda:
409 idempotency_in_flight. Espere e repita com a mesma chave.
Fila de emissão
A nota entra na fila e a fila roda a cada 10 minutos. Depois do despacho, a SEFAZ ou a prefeitura costuma responder em segundos. Não trave a tela do seu usuário esperando a autorização.
Limites
| Limite | Valor | Ao passar |
|---|---|---|
| Requisições por chave | 60 por minuto | 429 rate_limited com Retry-After |
| Emissões por CNPJ emitente | 20 por hora (janela deslizante) | 429 sefaz_quota_exceeded com Retry-After |
O limite por CNPJ protege o certificado do cliente contra bloqueio na SEFAZ Ele conta as emissões feitas pela API; emissões pelo painel, por lote e por recorrência não entram nessa conta.
Emitir nota
invoices:writeO campo tipo, na raiz do corpo, escolhe o documento e as regras aplicadas.
tipo | Documento | Exige |
|---|---|---|
nfse_nacional | NFS-e (roteada pelo município da empresa: Ambiente Nacional ou emissor de São Paulo) | Empresa habilitada para NFS-e |
nfe | NF-e modelo 55 | Empresa habilitada para NF-e |
cte, cte_os, cte_simp, mdfe, nfcom, dce | Documentos de transporte e comunicação | Habilitação e add-on contratado |
Resposta 202:
{
"id": "920d1d7a-f721-40d7-a44b-f79af97dd208",
"status": "queued",
"idempotency_key": "fatura-2026-000123",
"created_at": "2026-09-30T13:47:21.816Z"
}
// Header: Location: /v1/invoices/920d1d7a-f721-40d7-a44b-f79af97dd208NFS-e
Um só tipo para NFS-e. O EasyNotas decide o emissor pelo município da empresa: São Paulo capital usa o sistema da prefeitura, os demais usam o Ambiente Nacional.
{
"tipo": "nfse_nacional",
"cnpj_emitente": "12345678000190",
"tomador": {
"documento": "11222333000181",
"razao_social": "Cliente Exemplo LTDA",
"email": "financeiro@exemplo.com.br",
"endereco": {
"logradouro": "Rua das Flores", "numero": "100", "complemento": "Sala 2",
"bairro": "Centro", "municipio": "Araçatuba", "uf": "SP", "cep": "16010000"
}
},
"servico": {
"discriminacao": "Intermediação na venda do veículo Onix 2022",
"valor_servicos": 1500.00,
"codigo_tributacao_nacional": "100501",
"aliquota": 2.0,
"informacoes_complementares": "Veículo placa ABC1D23, chassi 9BG...",
"codigo_municipio_prestacao": "3502804",
"codigo_nbs": "102010000",
"codigo_indicador_operacao": "100301",
"ibs_cbs_situacao_tributaria": "000",
"ibs_cbs_classificacao_tributaria": "000001",
"iss_retido": false
}
}Tomador
| Campo | Obrig. | Regra |
|---|---|---|
tomador | não | Omitido, null ou {} = tomador não identificado (consumidor final). Tomador parcial, como nome sem documento, é recusado. |
tomador.documento | se houver tomador | CPF (11) ou CNPJ (14). Pontuação é ignorada. |
tomador.razao_social | se houver tomador | Até 150 caracteres. |
tomador.email | não | Recebe o PDF e o XML da nota. |
tomador.endereco | não | Tudo ou nada. Se enviar, logradouro, cep (8 dígitos), municipio e uf são obrigatórios; numero, complemento e bairro são opcionais. Sem endereço a nota sai com o tomador identificado só por documento e nome. |
Serviço
| Campo | Obrig. | Regra |
|---|---|---|
discriminacao | sim | 15 a 2000 caracteres. |
valor_servicos | sim | Maior que zero. |
codigo_tributacao_nacional | sim | 6 dígitos. Pode vir pontuado (10.05.01). |
aliquota | não | Alíquota do ISS em %. |
item_lista_servico | SP capital | Obrigatório quando a empresa é de São Paulo capital (código de serviço de 5 dígitos da prefeitura). |
codigo_tributacao_municipio | não | Código municipal, usado no emissor de São Paulo. |
informacoes_complementares novo | não | Até 2000 caracteres. No Ambiente Nacional vai no campo próprio da nota; em São Paulo é anexado à discriminação. |
codigo_municipio_prestacao novo | não | Código IBGE (7 dígitos) da cidade onde o serviço foi prestado. Sem ele, vale a cidade da empresa. |
codigo_nbs novo | não | 9 dígitos. Reforma Tributária. |
codigo_indicador_operacao novo | não | 6 dígitos. Com ele preenchido a SEFAZ Nacional passa a exigir o endereço completo do tomador. |
ibs_cbs_situacao_tributaria novo | não | CST do IBS/CBS, 3 dígitos. |
ibs_cbs_classificacao_tributaria novo | não | cClassTrib, 6 dígitos. |
iss_retido novo | não | true quando o tomador retém o ISS. Padrão false. |
retencoes novo | não | Objeto { valor_ir, valor_pis, valor_cofins, valor_csll } em reais. Veja abaixo. |
Os quatro campos da Reforma Tributária seguem a regra do painel: o que vier na nota vale; o que não vier sai do padrão cadastrado na empresa. Quando qualquer um deles está presente, o EasyNotas preenche sozinho a finalidade, o consumidor final e o indicador de destinatário exigidos pelo layout novo.
Retenções federais
Só se aplicam a emitente do Lucro Presumido ou Lucro Real. Retenção com valor para empresa do Simples ou MEI é recusada com 422 retencao_not_applicable.
| Você envia | O EasyNotas faz |
|---|---|
retencoes com valores | Usa os valores enviados. A soma precisa ser menor que o valor do serviço. No Ambiente Nacional, PIS, COFINS e CSLL retidos vão somados num só campo de retenção, como a SEFAZ Nacional exige, com CST de PIS/COFINS 01 e alíquotas de 0,65% e 3%; esse CST não é configurável pela API. |
retencoes ausente | Aplica o IRRF automático de 1,5%, igual ao painel, quando: empresa no Lucro Presumido/Real, serviço do item 10 da LC 116 (exceto 10.08, publicidade), tomador com CNPJ e IR acima de R$ 10,00 (na prática, nota a partir de R$ 667,00). |
retencoes: {} ou tudo zero | Nenhuma retenção, nem a automática. |
Campos de regime (codigo_opcao_simples_nacional, regime_especial_tributacao e parecidos) não são aceitos no corpo, porque vêm do cadastro. Enviar um deles responde 422 field_inherited_from_company.
NF-e modelo 55
{
"tipo": "nfe",
"cnpj_emitente": "12345678000190",
"destinatario": {
"nome": "Cliente Exemplo Ltda",
"documento": "98765432000188",
"ie": "123456789",
"contribuinte": true,
"email": "financeiro@exemplo.com.br",
"endereco": { "logradouro": "Rua das Palmeiras", "numero": "450", "bairro": "Centro",
"municipio": "Campinas", "uf": "SP", "cep": "13010000" }
},
"itens": [{
"codigo": "SKU-1042", "descricao": "Camiseta algodão branca M",
"quantidade": 2, "valor_unitario": 49.90,
"ncm": "61091000", "unidade": "UN", "cst_csosn": "102", "origem": 0
}],
"pagamento": [{ "forma": "01", "valor": 99.80 }],
"operacao": { "informacoes_adicionais_contribuinte": "Pedido 5531" }
}Destinatário e pagamento
| Campo | Obrig. | Regra |
|---|---|---|
destinatario.nome | sim | Até 150 caracteres. |
destinatario.documento | sim | CPF (11) ou CNPJ (14). |
destinatario.endereco | sim | logradouro, numero, bairro, municipio, uf e cep obrigatórios; complemento opcional. |
destinatario.ie | contribuinte | Inscrição estadual. CNPJ contribuinte de SP sem IE é rejeitado pela SEFAZ. |
destinatario.contribuinte | não | false para consumidor final. true sem IE válida gera rejeição. |
pagamento[] | não | { forma, valor }. Omitido, vira um pagamento único em dinheiro (01) no valor total. A forma 90 (sem pagamento) sempre vai com valor zero. A soma precisa bater com o total. Formas: 01, 02, 03, 04, 05, 10, 11, 12, 13, 15, 16, 17, 18, 19, 90, 99. |
Itens
De 1 a 200 itens por nota.
| Campo | Obrig. | Regra |
|---|---|---|
codigo | sim | Código do produto no seu sistema, até 60 caracteres. |
descricao | sim | |
quantidade, valor_unitario | sim | Quantidade maior que zero; valor maior ou igual a zero. |
cst_csosn | sim | Validado contra o regime da empresa. Simples/MEI: CSOSN 101, 102, 103, 201, 202, 203, 300, 400, 500, 900. Regime normal: CST 00, 10, 20, 30, 40, 41, 50, 51, 60, 70, 90. |
ncm | não | 8 dígitos. Sem ele vale o NCM padrão da empresa. |
cest, unidade | não | CEST malformado é recusado antes da SEFAZ. |
cfop | não | 4 dígitos. Sem ele, derivado de operação interna ou interestadual. Se informado, o primeiro dígito é ajustado à operação: 5202 vira 6202 para destinatário de outra UF, e vice-versa. |
origem | não | 0 a 8. Padrão 0. |
aliquota_icms | não | 0 a 100. Sem ela, vale a alíquota interna da empresa; se a empresa não tiver uma cadastrada, 18%. Confirme com o contador. |
numero_item_referenciado novo | devolução | Número do item (1 a 990) na NF-e original. |
codigo_beneficio_fiscal novo | não | cBenef, até 10 caracteres. |
pis_cst, cofins_cst novo | não | CST de 2 dígitos. Sem eles vale o padrão do regime. |
pis_aliquota, cofins_aliquota novo | não | 0 a 100. |
Operação (opcional)
| Campo | Regra |
|---|---|
serie novo | Sem ela, a NF-e usa a série exclusiva cadastrada na empresa. Isso evita colisão de numeração com outro emissor do cliente (rejeição 539). |
numero | Numeração manual. Sem ela, a numeração segue automática. |
natureza_operacao | Padrão "Venda de mercadoria". |
finalidade_emissao | 1 normal (padrão), 2 complementar, 3 ajuste, 4 devolução. |
chave_nfe_referenciada | Chave de 44 dígitos da nota original. Obrigatória na devolução. |
tipo_documento | 0 entrada, 1 saída (padrão). |
consumidor_final, presenca_comprador | Derivados do destinatário quando omitidos. |
modalidade_frete | Padrão 9 (sem transporte). |
valor_frete, valor_seguro, valor_outras_despesas, valor_desconto | Em reais. |
informacoes_adicionais_contribuinte | Até 5000 caracteres, no rodapé do DANFE. Texto maior é cortado. |
informacoes_adicionais_fisco | Até 2000 caracteres. Texto maior é cortado. |
data_emissao | Gerada pelo servidor no horário de São Paulo. Não recomendamos enviar. |
NF-e de devolução
A devolução tem quatro exigências da SEFAZ que o EasyNotas aplica sozinho. Você só precisa informar a finalidade 4, a chave da nota original e o item correspondente.
| Exigência da SEFAZ | O que o EasyNotas faz |
|---|---|
| CFOP de devolução só com finalidade 4 | Recusa antes de enviar quando o CFOP é de devolução e a finalidade não é 4. |
| Nota referenciada por item (DFeReferenciado) | Na devolução, referencia só por item. Mandar a referência no cabeçalho e no item ao mesmo tempo gera a rejeição 1010. |
| Grupo de ST retido em CSOSN 500 / CST 60 | Envia o grupo zerado, como a nota original. |
| Devolução sem pagamento | Força a forma 90 (sem pagamento), seja qual for o pagamento enviado. |
{
"tipo": "nfe",
"cnpj_emitente": "12345678000190",
"destinatario": {
"nome": "Supermercado Exemplo Ltda", "documento": "11444777000161",
"ie": "111222333444", "contribuinte": true,
"endereco": { "logradouro": "Av. Brasil", "numero": "1000", "bairro": "Centro",
"municipio": "Araçatuba", "uf": "SP", "cep": "16010000" }
},
"itens": [{
"codigo": "FERM-01", "descricao": "Fermento biológico", "quantidade": 10,
"valor_unitario": 9.98, "ncm": "21023000", "cst_csosn": "900",
"aliquota_icms": 18, "origem": 3, "cfop": "5202",
"numero_item_referenciado": 2
}],
"operacao": {
"natureza_operacao": "Devolução de compra",
"finalidade_emissao": 4,
"chave_nfe_referenciada": "35260911444777000161550010000123451123456780"
}
}Neste exemplo, o CSOSN 900 com alíquota de 18% destaca base de R$ 99,80 e ICMS de R$ 17,96. A chave de 44 dígitos, agrupada de 4 em 4, fica assim: 3526 0911 4447 7700 0161 5500 1000 0123 4511 2345 6780.
Transporte e comunicação
Para cte, cte_os, cte_simp, mdfe, nfcom e dce, o corpo fiscal vai dentro de documento, no mesmo formato que o painel usa. O bloco do emitente é sempre montado a partir do cadastro e substitui qualquer empresa enviada.
{
"tipo": "cte",
"cnpj_emitente": "12345678000190",
"documento": { /* campos do CT-e, iguais aos da tela de emissão */ }
}documento de cada tipo é passado pelo suporte do EasyNotas a quem for integrar transporte; esta página ainda não traz esses campos. Ainda sem emissão real pela API. Esses tipos usam os mesmos validadores do painel, mas nenhum foi emitido pela API em produção até agora. Teste em sandbox e acompanhe a primeira emissão real. Cada tipo exige o add-on correspondente no escritório (403 addon_required).Consultar e listar
invoices:read{
"id": "920d1d7a-f721-40d7-a44b-f79af97dd208",
"status": "authorized",
"tipo": "nfe",
"numero": "26",
"pdf_url": "https://…/danfe.pdf",
"xml_url": "https://…/35260911444777000161550010000123451123456780-nfe.xml",
"chave_acesso": "35260911444777000161550010000123451123456780",
"chave_nfe": "35260911444777000161550010000123451123456780",
"mensagem": null,
"created_at": "2026-09-30T13:47:21Z",
"updated_at": "2026-09-30T13:50:03Z"
}chave_acesso novo vem preenchida para documentos com chave de 44 dígitos (NF-e, CT-e, MDF-e…). Para NFS-e é null. chave_nfe continua existindo para integrações antigas. mensagem traz o motivo quando o status é rejected ou failed_internal.
invoices:readLista as notas do escritório, das mais novas para as mais antigas. page_size vai até 100 (padrão 20). A resposta traz data, page, page_size e has_more.
invoices:readDiz se a nota ainda está no prazo estimado de cancelamento. É informativo e não bloqueia nada. Leia o campo confiavel: quando é false, o prazo é estimativa. NF-e: 24 horas. Transporte: 7 dias. NFS-e: null, porque o prazo é municipal, e há prefeituras que não aceitam cancelamento por sistema.
Cancelar nota
invoices:cancel{ "justificativa": "Serviço não executado conforme contrato" }Justificativa de 15 a 255 caracteres. A resposta é 202 com status: "processing", e a confirmação chega pelo webhook invoice.cancelled, normalmente em segundos. O 202 quer dizer "pedido enviado", não "cancelada": até a confirmação, o GET continua mostrando authorized. Nota já cancelada responde 200 com already_cancelled: true. Nota que não está autorizada responde 409 not_cancelable.
Inutilizar numeração de NF-e novo
Quando um número de NF-e foi pulado, a SEFAZ exige que ele seja inutilizado. A chamada é síncrona e a resposta já traz o resultado da SEFAZ.
Idempotency-Key. Se a conexão cair, consulte o histórico (GET /v1/nfe/inutilizacoes) antes de enviar de novo.numbering:write{
"cnpj_emitente": "12345678000190",
"serie": "2",
"numero_inicial": 15,
"numero_final": 17,
"justificativa": "Numeração pulada por falha no sistema de origem"
}Regras: série de 1 a 3 dígitos, números inteiros maiores que zero, no máximo 1000 números por chamada e justificativa de 15 a 255 caracteres. Se algum número da faixa já foi transmitido pelo EasyNotas, inclusive em nota cancelada, a API recusa com 422 number_already_used antes de chegar à SEFAZ.
Autorizada, responde 201; recusada pela SEFAZ, 422. O corpo é o mesmo nos dois casos:
{
"id": "7c1e…",
"environment": "production",
"status": "authorized",
"serie": "2", "numero_inicial": 15, "numero_final": 17,
"protocolo_sefaz": "135260001234567",
"status_sefaz": "102",
"mensagem_sefaz": "Inutilização de número homologado"
}id é null).invoices:read ou numbering:writeHistórico de inutilizações de produção da empresa, feitas pelo painel ou pela API (campo origem: app ou api). Tentativas recusadas também aparecem, com status: "rejected". Paginado com page e page_size.
Notas recebidas novo
O Buscador XML do EasyNotas acompanha na SEFAZ as notas que fornecedores emitem contra o CNPJ de cada empresa do escritório. A API lê o que já foi sincronizado; ela não dispara uma nova busca.
received:read| Parâmetro | Regra |
|---|---|
cnpj_emitente | Obrigatório. CNPJ da sua empresa, a destinatária das notas. O nome segue o padrão das outras rotas. |
tipo | nfe, nfce, cte ou nfse. Sem filtro, lista todos menos eventos. |
desde, ate | Data de emissão, formato AAAA-MM-DD, fuso de São Paulo. |
manifestacao | pendente (sem manifestação), ciencia, confirmacao, desconhecimento ou nao_realizada. |
page, page_size | Padrão 1 e 20; máximo 100. |
{
"data": [{
"chave": "35260944555666000177550010000360621003500819",
"tipo": "nfe",
"emitente": { "cnpj": "44555666000177", "razao_social": "Distribuidora Exemplo Ltda" },
"cnpj_destinatario": "12345678000190",
"valor": 1280.40,
"data_emissao": "2026-09-12T10:31:00-03:00",
"situacao": "autorizada",
"nivel": "resumo",
"manifestacao": null,
"xml_disponivel": false,
"recebida_em": "2026-09-12T13:40:11Z"
}],
"page": 1, "page_size": 20, "has_more": false
}received:readBusca a nota pela chave em todas as empresas do escritório. A chave tem 44 dígitos (NF-e, NFC-e, CT-e) ou 50 dígitos (NFS-e do Ambiente Nacional). Quando o XML completo existe, a resposta traz xml_url, um link de download válido por 10 minutos (xml_url_expires_in: 600). Baixe na hora e não guarde o link.
nivel: "resumo", com dados básicos.Manifestação do destinatário novo
received:manifest{
"cnpj_emitente": "12345678000190",
"tipo": "ciencia"
}tipo | Documento | Justificativa | Libera o XML |
|---|---|---|---|
ciencia | NF-e | não | sim |
confirmacao | NF-e | 15 a 255 caracteres | sim |
desconhecimento | NF-e | não | não |
nao_realizada | NF-e | 15 a 255 caracteres | não |
desacordo | CT-e | 15 a 255 caracteres | não |
Resposta 200:
{
"chave": "35260944555666000177550010000360621003500819",
"manifestacao": "ciencia",
"xml_disponivel": true,
"consumiu_franquia": true
}É o mesmo processo do botão do painel: registra o evento na SEFAZ, baixa o XML quando liberado e consome a franquia do plano quando o XML é baixado. Uma nota só pode ser manifestada uma vez: a segunda chamada responde 409 already_manifested.
403 not_available_in_sandbox). No log do EasyNotas o evento fica registrado com origem api, em nome do dono do escritório que gerou a chave.Webhooks
Cadastre uma URL em Configurações → API / Desenvolvedores → Webhook. O EasyNotas faz um POST quando uma nota criada por aquela chave chega a um estado final.
| Evento | Quando |
|---|---|
invoice.authorized | Nota autorizada; PDF e XML disponíveis. |
invoice.rejected | Rejeitada pela SEFAZ ou pela prefeitura. |
invoice.cancelled | Cancelamento confirmado. |
POST https://seu-sistema.com.br/webhooks/easynotas
X-EasyNotas-Event: invoice.authorized
X-EasyNotas-Delivery: 9f2c6a1e-… // igual em todas as tentativas desta entrega
X-EasyNotas-Signature: t=1790780400,v1=8a1f…
{
"event": "invoice.authorized",
"created_at": "2026-09-30T13:50:03Z",
"data": {
"id": "920d1d7a-…", "status": "authorized", "tipo": "nfe",
"environment": "production", "numero_nota": "26",
"chave_nfe": "35260911444777000161550010000123451123456780",
"pdf_url": "https://…", "xml_url": "https://…",
"emitida_at": "2026-09-30T13:50:01Z", "cancelada_at": null
}
}O webhook traz a chave de acesso no campo chave_nfe (só dígitos), o mesmo valor que o GET devolve em chave_acesso.
Validando a assinatura
A assinatura é um HMAC-SHA256 de {t}.{corpo}, em hexadecimal, com o segredo whsec_… mostrado ao cadastrar o webhook. Cada vez que a URL é salva, um segredo novo é gerado e o anterior deixa de valer: atualize os dois juntos no seu sistema. Use o corpo bruto, exatamente como chegou, e compare em tempo constante.
const crypto = require('crypto');
function assinaturaValida(rawBody, header, secret) {
const partes = Object.fromEntries(header.split(',').map((p) => p.split('=')));
const esperado = crypto.createHmac('sha256', secret)
.update(`${partes.t}.${rawBody}`)
.digest('hex');
const a = Buffer.from(partes.v1 || '', 'hex');
const b = Buffer.from(esperado, 'hex');
if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) return false;
return Math.abs(Date.now() / 1000 - Number(partes.t)) < 300; // recusa avisos com mais de 5 min
}Reentrega
A entrega pode chegar mais de uma vez. Responda 2xx em até 10 segundos e processe depois. São 6 tentativas no total: a primeira na hora e as outras após 30 s, 2 min, 8 min, 32 min e cerca de 2 h. Depois disso a entrega é dada como falha. Redirecionamentos (301/302) contam como falha: a URL precisa responder 2xx direto. As entregas chegam com User-Agent: EasyNotas-Webhooks/1. Use X-EasyNotas-Delivery para descartar duplicatas. A URL precisa ser HTTPS pública na porta 443; endereços internos (localhost, 10.x, 172.16–31.x, 192.168.x, 169.254.x, 100.64.x, IPv6 local, domínios .local/.internal) e URLs com usuário e senha são recusados.
failed_internal não gera webhook. Se uma nota falhar antes de chegar à SEFAZ, nenhum aviso é enviado. Para notas sem evento depois de 30 minutos, consulte GET /v1/invoices/{id}.Estados fiscais
| Status | Significado | O que fazer |
|---|---|---|
| queued | Na fila de emissão. | Aguardar. |
| processing | Em autorização. | Consultar respeitando Retry-After. |
| authorized | Autorizada; PDF e XML disponíveis. | Usar pdf_url e xml_url. |
| rejected | Rejeitada pela SEFAZ ou prefeitura. | Ler mensagem, corrigir e emitir com outra Idempotency-Key. |
| failed_internal | Falha antes de chegar à SEFAZ. Não gera webhook. | Reenviar ou acionar o suporte. |
| cancelled | Cancelada. | Nada. |
Códigos de erro
Todo erro vem como { "error": { "code", "message", "field"? } }. Nos erros de validação, field diz exatamente qual campo corrigir.
| HTTP | code | Quando |
|---|---|---|
| 400 | missing_environment | Falta o header X-EasyNotas-Environment. |
| 400 | invalid_idempotency_key | Header ausente ou fora do formato. |
| 400 | invalid_json | Corpo não é JSON válido. |
| 400 | invalid_status_filter, invalid_manifestacao_filter, invalid_date_filter | Filtro inválido numa listagem. |
| 400 | invalid_tipo_filter | Filtro tipo inválido em /v1/received. |
| 400 | missing_cnpj_emitente | Falta ?cnpj_emitente= nas rotas de inutilização e recebidas. |
| 401 | missing_api_key, invalid_api_key, expired | Chave ausente, inválida ou vencida. |
| 402 | payment_required | Escritório bloqueado ou com pagamento pendente. |
| 403 | revoked, environment_mismatch | Chave revogada, ou header de ambiente diferente do ambiente da chave. |
| 403 | insufficient_scope | A chave não tem o escopo da rota. |
| 403 | company_not_in_office | O CNPJ não é de uma empresa do seu escritório. |
| 403 | note_type_not_enabled, addon_required | Empresa não habilitada para o tipo, ou add-on de transporte não contratado. |
| 403 | not_available_in_sandbox novo | Manifestação chamada com chave de sandbox. |
| 404 | not_found, route_not_found | Nota ou rota inexistente. |
| 409 | idempotency_conflict, idempotency_in_flight | Veja Idempotência. |
| 409 | not_cancelable | A nota não está autorizada. |
| 409 | already_manifested novo | A nota recebida já tem manifestação. |
| 422 | invalid_payload | Campo ausente ou inválido. Veja field. |
| 422 | invalid_fiscal_payload | O validador fiscal recusou (soma de pagamentos, CFOP de devolução sem finalidade 4, etc.). |
| 422 | field_inherited_from_company | Campo de regime enviado no corpo. |
| 422 | unsupported_note_type | tipo fora da lista. |
| 422 | retencao_not_applicable novo | Retenção federal para empresa do Simples ou MEI. |
| 422 | invalid_justificativa | Justificativa de cancelamento fora de 15–255 caracteres. |
| 422 | number_already_used novo | Faixa de inutilização contém número já transmitido. |
| 422 | invalid_manifest_type, manifest_rejected novo | Tipo de manifestação incompatível com o documento, ou recusa da SEFAZ. |
| 424 | certificate_missing | Empresa sem certificado digital. |
| 424 | focus_not_configured, focus_token_missing novo | Empresa ainda não configurada no emissor fiscal. Acione o suporte. |
| 429 | rate_limited, sefaz_quota_exceeded | Respeite Retry-After. O rate_limited também traz X-RateLimit-Limit e X-RateLimit-Remaining. |
| 500 | internal_error e similares | Falha do EasyNotas. Se persistir, acione o suporte. |
| 502 | cancel_dispatch_failed | Falha ao encaminhar o cancelamento. A nota segue ativa; tente de novo. |
| 503 | quota_unavailable, cancel_unavailable, numbering_unavailable, manifest_unavailable | Indisponibilidade temporária. Tente de novo com backoff. |
Limitações conhecidas
- Documentos de transporte ainda sem emissão real pela API.
- Carta de correção (CC-e) é feita só pelo painel.
tomador.codigo_municipioé validado, mas o município do tomador é resolvido pelo nome da cidade.- Os prazos de cancelamento da janela são estimativas; quem decide é a SEFAZ ou a prefeitura.
- A API lê as notas recebidas já sincronizadas; a sincronização com a SEFAZ roda no próprio EasyNotas.
- O domínio da API é o do provedor de infraestrutura. Um domínio próprio pode ser adotado no futuro, com aviso prévio.
OpenAPI
A especificação OpenAPI 3.1 (versão 1.1.0) cobre todas as rotas, os schemas e os erros. Ela está publicada em /openapi.yaml. Com ela você gera um cliente tipado na sua linguagem:
# TypeScript
npx openapi-typescript openapi.yaml -o easynotas.d.ts
# C#, PHP, Python, Java: troque o -g
npx @openapitools/openapi-generator-cli generate \
-i openapi.yaml -g csharp -o ./easynotas-clientPara navegar visualmente, abra o arquivo em editor.swagger.io.
Termos de Uso
O uso da API segue os Termos de Uso da API, aceitos por quem gera a chave no painel.
Suporte
Dúvidas de integração, habilitação de tipos de nota e configuração de empresas: fale com o time do EasyNotas pelo canal de suporte do seu escritório.