Referência da APIEmissão de NFS-e para empresas da Ext Contabilidade. REST, JSON, valores em centavos. A emissão é assíncrona: 202 e depois polling, com um envelope de erro único em toda falha.
POST /v1/nfse com amount_in_cents e description. Mande também reference — o id da fatura no seu sistema —, que é por onde você reencontra a nota se o id se perder. Emitindo fora da atividade principal da empresa, mande cnae (7 dígitos) — e service_code (6) junto, se aquele CNAE tiver mais de uma linha. Omitidos, a nota sai na principal.id do 202. Ele significa ACEITA, não autorizada.Location no intervalo do Retry-After até o status virar um desfecho.issued, baixe GET /v1/nfse/{id}/pdf e GET /v1/nfse/{id}/xml.Os dois tropeços mais comuns da primeira integração: amount_in_cents é em CENTAVOS (R$ 1.250,50 = 125050), e failed e indeterminate pedem ações opostas.
Authorization: Bearer ext_sk_...
Não há auto-contratação: quem libera a empresa é o suporte ([email protected]), e antes disso toda rota responde 403 feature_not_enabled. Liberada, as chaves saem em Minha conta → API, exibidas uma única vez.
A chave carrega o modo, e ele volta em livemode em toda resposta — inclusive no envelope de erro:
ext_sk_test_... — a nota é emitida de verdade, na produção restrita do Emissor Nacional: o ambiente do Sistema Nacional da NFS-e sem validade fiscal. Expira em 7 dias, não é cobrada e não consome sequencial da produção. Pode terminar failed — o status vem do SEFIN, o sistema nacional que autoriza as notas, e a rejeição chega em error.message com o código E0xxx.ext_sk_live_... — documento fiscal já na primeira chamada. Desfazer exige cancelamento formal, que tem prazo.O código é o mesmo nos dois: integre com a chave de teste e troque a variável de ambiente para ir a produção. Os modos não se enxergam — o que não é do modo da chave responde 404.
O modo de teste é MAIS exigente, de propósito: ele recusa nfse.municipal_registration_missing e nfse.nbs_invalid, pendências de cadastro que a emissão real aceita para o SEFIN rejeitar depois. Em troca não prova tudo — o que fica fora do alcance dele está em POST /v1/nfse.
O 202 diz ACEITA, não autorizada. Consulte o Location até chegar a um DESFECHO — nem todo status é um:
status | Desfecho? | O que fazer |
|---|---|---|
queued | não | Aceita, ainda não enviada. Continue consultando. |
processing | não | Envio em curso. Transitório — nunca o trate como desfecho. |
issued | sim | Autorizada. access_key e links existem: baixe PDF e XML. |
failed | sim | Recusada, e nenhum documento fiscal existe. Corrija o que error aponta e emita de novo: é seguro. |
indeterminate | sim | Pode existir NFS-e autorizada sem registro aqui. Nunca reemita — acione o suporte. |
canceled | sim | Cancelamento confirmado. Nota em cancelamento ainda aparece como issued. |
Reemitir uma nota já autorizada gera documento fiscal em duplicidade, que só se desfaz com cancelamento formal — e cancelamento tem prazo.
switch (nfse.status) {
case 'queued':
case 'processing':
return agendarNovaConsulta(nfse); // ainda não é desfecho
case 'issued':
return guardar(nfse);
case 'failed':
return corrigirEReemitir(nfse); // não há documento: é seguro
case 'indeterminate':
return abrirChamado(nfse); // pode existir: NÃO reemitir
default:
return abrirChamado(nfse); // status desconhecido: NÃO reemitir
}O default é a parte que importa: status novo pode aparecer, e o desfecho seguro para um desconhecido é sempre não reemitir.
Toda falha sai no mesmo envelope: type (família), code (detalhe), message, param, retryable e request_id — cite o request_id ao abrir chamado. Trate pelo code, nunca pelo status HTTP: o mesmo 422 cobre situações muito diferentes.
code é uma lista aberta: código novo nasce quando uma recusa genérica ganha nome próprio, e acrescentar um não é breaking change — por isso o seu switch precisa de um ramo default. Código já publicado nunca é renomeado nem muda de significado. type é fechado: são as famílias do enum no schema, e uma nova só entra com versão nova da API.
O catálogo completo está na seção Erros, ao fim desta referência.
60 requisições de escrita e 300 de leitura por 60 segundos, por chave, em baldes SEPARADOS: o POST da emissão gasta de um, as consultas e os downloads do outro — o polling não gasta a cota de emissão. São piso garantido, não teto.
Toda resposta traz RateLimit-Limit, RateLimit-Remaining e RateLimit-Reset; excedido o limite, 429 com Retry-After. Corpo de até 10 MB.
No Retry-After: 3 que a API sugere, cada nota em voo gasta 20 leituras por minuto: até 15 notas simultâneas cabem obedecendo à dica ao pé da letra. Acima disso, espace o polling.
Emissão, consulta, cancelamento e download de notas fiscais de serviço. A emissão é assíncrona: o 202 diz que a nota foi aceita, não que foi autorizada.
Todo code que a API emite hoje, com o status HTTP e o que fazer em cada um. O envelope e a regra de tratamento estão em Erros, na introdução.
code | HTTP | O que fazer |
|---|---|---|
unauthorized | 401 | Chave ausente, inválida ou revogada. |
feature_not_enabled | 403 | A empresa não tem a API liberada. Fale com o suporte. |
insufficient_scope | 403 | A chave não tem o escopo da rota. Gere outra com o escopo certo. |
company.issuer_not_enabled | 422 | Emissão automática desligada para a empresa. |
company.issuer_not_configured | 422 | Emissor Nacional não configurado. |
company.issuer_not_approved | 422 | A empresa ainda não foi liberada para emitir. Acione o suporte. |
company.certificate_missing | 422 | Certificado ausente ou vencido no cofre. |
nfse.cnae_missing_ctribnac | 422 | O CNAE principal não tem código de tributação mapeado. Acione o suporte. |
nfse.cnae_missing_nbs | 422 | O CNAE principal não tem código NBS mapeado. Acione o suporte. |
nfse.municipality_not_covered | 422 | Cidade/UF do cadastro não resolvem para código IBGE. |
nfse.cnae_not_allowed | 422 | O cnae enviado não está no cadastro da empresa na Receita, ou a linha padrão dele está bloqueada. Mande outro, ou omita o campo para sair na atividade principal. |
nfse.cnae_without_default | 422 | O cnae tem linha no catálogo, mas nenhuma é a padrão. Mande o service_code da linha que você quer, ou acione o suporte. |
nfse.service_code_unknown | 422 | O service_code não existe no catálogo. Ele tem 6 dígitos (010501) — o clássico é mandar aí o CNAE, que tem 7. |
nfse.service_code_not_allowed | 422 | O service_code existe, mas não está entre as linhas da empresa. Mandando o par, cnae e service_code precisam ser da MESMA linha. |
nfse.nbs_invalid | 422 | Só em livemode: false. O código NBS do CNAE não consta da tabela oficial. Em produção a nota é aceita e o SEFIN rejeita depois (E0316). |
nfse.municipal_registration_missing | 422 | Só em livemode: false. O município exige a Inscrição Municipal do prestador e ela não está cadastrada. Em produção a nota é aceita e o SEFIN rejeita depois (E0120). |
nfse.customer_document_invalid | 400 | CPF/CNPJ com máscara ou fora de 11/14 dígitos. |
nfse.customer_address_malformed | 400 | customer.address em br com CEP fora de 8 dígitos, state que não é sigla de UF, ou cidade que não resolve na tabela do IBGE. Diferente de nfse.customer_address_invalid — este é erro de FORMA, pego na criação; aquele é o SEFIN recusando o CEP depois. |
nfse.customer_required | 422 | O município do prestador exige a identificação do tomador e a nota veio sem customer.document. Vale nos dois modos — nos demais municípios, nota sem tomador continua válida. |
nfse.customer_name_required | 422 | Tomador pessoa física (CPF) sem customer.name: o nome é impresso na nota e não pode ficar vazio, e não há cadastro público de nome de PF de onde tirá-lo. Em CNPJ é opcional — ali o nome sai do cadastro da Receita. |
nfse.customer_address_required | 422 | Desde o leiaute da NT 009 (IBS/CBS), nos serviços em que o imposto é devido no domicílio de quem contrata (a maioria: TI, consultoria, licenciamento…), a nota precisa do tomador: customer.document sempre e, se for CPF, customer.address com o CEP. Em CNPJ o endereço sai do cadastro da Receita. Serviço presencial (saúde, academia) e exportação não entram. param diz o campo que falta. |
nfse.export_incomplete | 422 | Falta campo obrigatório da exportação. A mensagem lista todos os ausentes. |
nfse.country_unsupported | 422 | País do tomador fora da Tabela de Países (ISO alpha-2). |
nfse.currency_unsupported | 422 | Moeda fora da lista aceita. Use a sigla ISO (USD, EUR, GBP). |
nfse.foreign_amount_invalid | 422 | Valor na moeda estrangeira ausente, zero ou negativo. |
nfse.customer_type_conflict | 422 | Campo incompatível com o customer.type declarado. |
nfse.competence_invalid | 400 | Competência não é uma data existente. Use o formato YYYY-MM-DD (2026-02-30 não existe). |
nfse.competence_out_of_window | 422 | Competência fora do prazo: vale o mês corrente, e o mês anterior só até o dia 7 (inclusive). Nunca no futuro. |
nfse.iss_retido_indisponivel | 422 | A emissão com ISS retido pelo tomador ainda não é automática. A Ext emite esta nota para você pelo Emissor Nacional: envie o pedido pelo chat. A nota não foi criada; não reenvie sem o campo, que ela sairia como não retida. |
nfse.iss_withholding_not_allowed | 422 | A retenção não vale para esta nota, e a message diz por quê: tomador sem CNPJ (CPF, sem tomador ou no exterior), prestador MEI ou regime que ainda não emite com retenção. A nota não foi criada. Siga a orientação da message; não reenvie sem o campo só para passar, que a nota sairia como não retida. |
nfse.federal_withholding_unavailable | 422 | A emissão com retenção de impostos federais ainda não é automática. A Ext emite esta nota para você pelo Emissor Nacional: envie o pedido pelo chat. A nota não foi criada; não reenvie sem o campo, que ela sairia sem a retenção que o tomador vai fazer. |
nfse.federal_withholding_not_allowed | 422 | A retenção federal não vale para esta nota, e a message diz por quê: empresa fora do Lucro Presumido ou tomador sem CNPJ no Brasil. A nota não foi criada. Siga a orientação da message. |
nfse.service_location_required | 422 | A linha da nota exige o local da execução: serviço com ISS devido no local (LC 116, art. 3º) ou atividade que registra o local. Mande service_location_ibge com o código IBGE do município onde o serviço foi executado. |
nfse.service_location_invalid | 422 | O service_location_ibge tem 7 dígitos, mas não é município da tabela do IBGE. Confira o código do município da execução. |
nfse.cancel_window_closed | 422 | O prazo de cancelamento passou: até o dia 7 do mês seguinte à competência da nota. Fale com o suporte — ainda pode ser possível. |
nfse.not_cancelable | 409 | Estado incompatível: nota não autorizada, substituída, ou anexada manualmente. Só nota autorizada por esta API é cancelável por aqui. |
nfse.already_canceled | 409 | Esta nota já foi cancelada — nada a fazer. |
nfse.cancel_in_progress | 409 | Já há um pedido de cancelamento em andamento. Não repita o POST: o cancelamento é irreversível, e o segundo pedido seria rejeitado. Consulte a nota para acompanhar. |
nfse.not_issued | 409 | Pediu PDF ou XML de nota ainda não autorizada. Repetível quando a nota está queued ou processing — ali o envelope vem com retryable: true e Retry-After. Em failed e indeterminate vem retryable: false: o arquivo não nasce sozinho. |
nfse.reference_in_use | 409 | Já existe nota desta empresa com esse reference, e a mensagem traz o id dela. É a idempotência do reference funcionando. Para uma segunda nota intencional, use outra referência. |
duplicate_suspected | 409 | Requisição idêntica há menos de 60s. Reenvie com Idempotency-Key própria, ou com reference diferente, se a segunda nota é intencional. |
idempotency_key_in_use | 409 | A primeira requisição com essa chave, e com o MESMO corpo, ainda está em voo. Repetível. |
idempotency_key_reuse | 409 | Mesma Idempotency-Key com corpo diferente, em qualquer momento das 24h da chave. Use uma chave nova. |
invalid_request | 400 | Validação do corpo: campo obrigatório ausente, tipo errado ou campo desconhecido (o clássico é amount em vez de amount_in_cents). message traz a primeira causa; errors[], todas. |
invalid_id | 400 | O id não é um UUID (com ou sem o prefixo nfse_). |
not_found | 404 | Não há nota com esse id para a empresa da chave. |
route_not_found | 404 | Rota inexistente sob /v1. |
malformed_json | 400 | Corpo não é JSON válido. |
payload_too_large | 413 | Corpo acima de 10 MB. |
unsupported_media_type | 415 | Falta Content-Type: application/json. |
rate_limit_exceeded | 429 | Estourou o limite. Espere o Retry-After. Repetível. |
internal_error | 500 | Falha nossa, não mapeada. Cite o request_id ao abrir chamado. Repetível. |
service_unavailable | 503 | Indisponibilidade temporária nossa. Repetível. |
As linhas marcadas como repetíveis são as únicas com retryable: true, e nfse.not_issued é CONDICIONAL, pelo status da nota. Não deduza retryable do code: leia o campo do envelope e obedeça ao Retry-After.
Authorization: Bearer ext_sk_...Encontra a nota pelo reference que você enviou na emissão — o caminho de volta quando o id se perde.
Devolve um envelope de lista com no máximo uma nota, já que reference é único por empresa dentro de cada modo. Sem resultado, 200 com data vazio — nunca 404. Como toda leitura desta API, é escopada pelo MODO da chave.
referencestringobrigatórioO reference que você enviou na emissão. Obrigatório — esta rota é busca por referência, não listagem: sem ele a resposta é 400 invalid_request.
Como reference é único por empresa dentro de cada modo, o resultado tem no máximo uma nota. Sem resultado, é 200 com data vazio.
Envelope de lista. data traz a nota encontrada, ou vem vazio.
Payload inválido: campo mal formatado, ausente ou fora do domínio aceito. Corrigir e reenviar resolve.
Chave de API ausente, mal formada, revogada ou inexistente. Nenhuma nota é criada.
A chave é válida mas não pode fazer isto: falta o escopo exigido, ou a API pública está desligada para a empresa.
Limite de requisições por chave excedido. Aguarde o que o header Retry-After indicar.
Falha nossa. retryable é true: repita com a MESMA Idempotency-Key para não duplicar a nota, e cite o request_id se persistir.
curl 'https://api.extcontabilidade.com.br/v1/nfse?reference=INV-2026-0042' \
-H 'Authorization: Bearer ext_sk_...'{
"object": "list",
"has_more": false,
"data": [
{
"object": null,
"id": null,
"livemode": null,
"status": null,
"amount_in_cents": null,
"failed_attempts": null
}
]
}Com uma chave ext_sk_live_, esta chamada emite uma NFS-e real, e desfazê-la exige cancelamento formal, que tem prazo. Para ensaiar a integração inteira use ext_sk_test_: mesmo corpo, mesmo 202, mesmo polling, com livemode: false.
Devolve 202 na hora — a emissão é assíncrona. O Location traz a rota de consulta e o Retry-After, o intervalo de polling. O 202 diz ACEITA, não autorizada: acompanhe o status até issued ou failed. Recusas de cadastro chegam aqui, de forma síncrona, antes do 202.
A nota de teste sai na produção restrita do Emissor Nacional: outro ambiente, com cadastro municipal próprio. Ela é emitida de verdade e pode falhar — e uma rejeição de lá é informação sobre o ambiente, não sobre o seu payload. Fora do alcance do teste ficam:
description narra: ele é resolvido pelo CNAE, nunca pelo texto. Os dois ambientes aceitam a nota do mesmo jeito.404, não nfse.cancel_window_closed.cancellation.status: "failed" é permanente em produção — um novo POST /cancel responde 409 nfse.cancel_in_progress, sem saída pela API —, mas libera nova tentativa no modo de teste. Não use o teste para prever a produção neste ponto.cancellation.error.message traz o motivo do SEFIN; em produção é sempre a genérica "não recebemos a confirmação", porque lá a incerteza é real. Não faça parsing da mensagem — trate pelo cancellation.status.Idempotency-KeystringChave escolhida por você para tornar a emissão segura de repetir. Opcional, e altamente recomendada em qualquer cliente com retry automático. Use uma chave nova por nota.
• Mesmo corpo + mesma chave devolve a primeira resposta, sem emitir de novo — é o que torna o retry de rede seguro.
• Corpo diferente com a mesma chave é 409 idempotency_key_reuse, por toda a janela de 24h: a chave identifica UMA emissão.
• Mesmo corpo, primeira ainda em voo, é 409 idempotency_key_in_use, que é repetível: aguarde e consulte antes de reenviar.
• Janela de 24h. Passada, a chave é esquecida e o mesmo corpo emite uma segunda nota.
Sem o header não há replay. A API ainda barra duplicata evidente — mesmo valor e mesma descrição em menos de um minuto — com 409 duplicate_suspected, mas essa rede é frouxa: não pega o retry que chega depois do minuto nem duas chamadas simultâneas, e recusa duas notas legitimamente iguais no mesmo minuto.
Melhor ainda: mande reference. É a única proteção que não expira — sendo único por empresa, a segunda emissão com a mesma referência é 409 nfse.reference_in_use, trazendo o id da original. Os dois se somam: a Idempotency-Key REPETE a resposta no retry imediato; o reference RECUSA a segunda nota para sempre.
amount_in_centsintegerobrigatórioValor total do serviço em centavos, inteiro. R$ 1.250,50 se envia como 125050 — nunca 1250.50, que é recusado com 400 em vez de virar R$ 12,50 em silêncio. Mínimo 100 (R$ 1,00), máximo 9007199254740991. É o valor que vai para a nota, sem retenções.
descriptionstringobrigatórioDescrição do serviço, impressa na nota. Entre 1 e 2000 caracteres — o teto é da nota fiscal, não nosso.
Sai como enviado, com UMA exceção: os marcadores {{data}}, {{mes}}, {{mes_extenso}}, {{mes_anterior}}, {{mes_anterior_extenso}} e {{ano}} são substituídos na emissão, resolvidos na data em que a chamada é recebida. Texto sem {{ passa intacto.
customerobjetoTomador do serviço. Omitir o objeto inteiro emite uma nota sem tomador, válida só nos serviços em que o IBS/CBS é devido no estabelecimento do prestador (presenciais, como saúde e academia).
Leiaute NT 009 (IBS/CBS): nos serviços em que o imposto é devido no domicílio de quem contrata — a maioria —, a nota sem customer.document, ou de CPF sem customer.address, é recusada na hora com 422 nfse.customer_address_required, antes do 202. Vale já com a chave de teste e, na de produção, a partir da virada do Emissor Nacional (01/12/2026).
Alguns municípios exigem o tomador em qualquer serviço: neles, a nota sem customer.document é recusada com 422 nfse.customer_required.
Tomador no exterior é exportação de serviço, e sai por aqui: mande customer.type: "foreign" com customer.address completo, a identificação fiscal (customer.tax_id ou customer.tax_id_absence_reason) e foreign_amount. Sem o type, o tomador é tratado como brasileiro — omitir customer continua significando *nota sem tomador*, e não *tomador no exterior*.
typestring: br | foreignOnde está o tomador. br (padrão) é o comportamento de sempre — identificação por CPF/CNPJ, operação doméstica tributável. foreign faz a nota sair como exportação de serviço: sem ISS, com o grupo de comércio exterior, e exigindo customer.address completo, customer.tax_id (ou customer.tax_id_absence_reason) e foreign_amount.
Omitir o campo é o mesmo que br. Nenhum payload que funciona hoje muda de comportamento.
documentstringCPF (11 dígitos) ou CNPJ (14), apenas dígitos — máscara com pontos, barra ou traço é RECUSADA com 400, não limpa.
namestringNome do tomador. Obrigatório quando customer.document é um CPF — o nome é impresso na nota, não há cadastro público de nome de pessoa física, e sem ele a nota é 422 nfse.customer_name_required. Em CNPJ é opcional: vence o nome do cadastro da Receita, e este campo é o fallback se a consulta não resolver.
tax_idstringNúmero de identificação fiscal do tomador no país dele (NIF). Exportação exige este campo ou customer.tax_id_absence_reason — nunca os dois vazios.
tax_id_absence_reasonstring: exempt | not_requiredMotivo de o tomador não ter identificação fiscal informada. exempt = dispensado; not_required = não exigência no país dele. Use quando não houver customer.tax_id.
addressobjetoEndereço do tomador. Obrigatório em foreign. Em br, obrigatório para CPF nos serviços em que o IBS/CBS é devido no domicílio de quem contrata (leiaute NT 009: TI, licenciamento, consultoria e a maioria dos serviços; não entram os presenciais, como saúde e academia) — sem ele, 422 nfse.customer_address_required. Nos demais casos é opcional: quando enviado, substitui em bloco o endereço do cadastro da Receita Federal — use quando o CEP cadastral for recusado pela prefeitura (nfse.customer_address_invalid).
streetstringobrigatórioLogradouro do tomador (xLgr da DPS).
numberstringobrigatórioNúmero do endereço (nro da DPS).
districtstringobrigatórioBairro ou distrito (xBairro da DPS). Obrigatório pelo leiaute, mesmo onde o endereço local não usa bairro.
citystringobrigatórioCidade. Em br, usada com state para resolver o município no IBGE — nome oficial, sem abreviação. Em foreign: texto livre (xCidade da DPS).
statestringobrigatórioEstado, província ou região. Em br: sigla da UF (2 letras) — usada para resolver o município no IBGE, junto de city. Em foreign: texto livre (xEstProvReg da DPS).
postal_codestringobrigatórioCódigo postal. Em br: CEP com 8 dígitos, sem hífen. Em foreign: formato livre (cEndPost da DPS).
countrystringobrigatórioPaís do tomador em ISO 3166-1 alpha-2 — dois caracteres, como US, PT, BR ou MZ. Em foreign vai para endExt/cPais e para tribMun/cPaisResult, o país em que o resultado do serviço se verifica. Em br o único valor aceito é BR; em foreign, qualquer país que não seja o Brasil.
foreign_amountobjetoValor da nota na moeda do faturamento. Obrigatório quando customer.type é foreign; recusado fora da exportação.
Não substitui amount_in_cents. Os dois são exigidos: o valor em reais é o que vai para a nota e para a apuração, e a API não converte câmbio — a taxa é decisão sua.
currencystringobrigatórioSigla ISO de três letras da moeda do faturamento — USD, EUR, GBP. Não é o código numérico do BACEN: a tradução é nossa. Moeda fora da lista aceita é 422 nfse.currency_unsupported, com as siglas no corpo do erro.
amount_in_centsintegerobrigatórioValor na moeda estrangeira em centavos, número inteiro. 3.600,00 se envia como 360000.
Duas casas decimais para qualquer moeda, inclusive as que não têm centavo (iene) ou têm três casas (dinar kuwaitiano): é o que a DPS recebe, e prometer outra precisão seria prometer o que a emissão não carrega.
referencestringSeu identificador para esta nota — tipicamente o id da fatura no seu sistema. Opcional, e a forma mais confiável de reencontrar a nota: o id que devolvemos se perde se o seu processo morrer antes de gravá-lo, e a Idempotency-Key é esquecida em 24h.
• Único por empresa, dentro de cada modo. Reenviar o mesmo é 409 nfse.reference_in_use, com o id da nota que já existe na mensagem — o retry nunca vira nota duplicada, sem prazo de validade. Precisando de duas notas para a mesma fatura, use referências diferentes.
• A mesma referência serve uma vez em teste e uma em produção: os modos não se enxergam.
• Consultável em GET /v1/nfse?reference=....
• Até 64 caracteres, sem espaços — espaço é recusado em vez de aparado, porque "INV-1 " e "INV-1" seriam chaves diferentes.
Com Idempotency-Key junto, os dois se somam: o retry imediato ganha o REPLAY da chave, e só uma referência de verdade repetida cai no 409.
competencestringCompetência da nota (dCompet), no formato YYYY-MM-DD. Opcional.
• Omitido, a competência é a data desta chamada — o certo para a maioria.
• Enviado, precisa ser do mês corrente, ou do mês anterior até o dia 7 (inclusive, 23:59:59 de Brasília). Fora disso é 422 nfse.competence_out_of_window.
• Nunca no futuro — nem um dia futuro do mês corrente.
• Data que não existe (2026-02-30) é 400 nfse.competence_invalid.
O competence da resposta confirma em que competência a nota saiu.
cnaestringCNAE em que a nota deve sair, 7 dígitos, apenas números (a máscara 6202-3/00 é recusada com 400). Opcional.
• Omitido, a nota sai na atividade principal — o certo para a maioria.
• Enviado, precisa estar no cadastro da empresa na Receita Federal, principal ou secundária; um que não esteja é 422 nfse.cnae_not_allowed.
• A nota sai na linha padrão daquele CNAE; precisando de outra, mande service_code junto. CNAE sem linha padrão é 422 nfse.cnae_without_default.
O cnae e o service_code da resposta confirmam a linha em que a nota saiu.
service_codestringCódigo de tributação nacional da linha, 6 dígitos, apenas números. Opcional, e o desempate do cnae — não confunda os dois: aqui são 6 dígitos (010501), não os 7 do CNAE.
A linha da nota sai desta tabela:
| o que você manda | linha da nota |
|---|---|
| nada | a padrão da atividade principal da empresa |
cnae | a padrão daquele CNAE |
cnae + service_code | a linha exata |
service_code | a primeira linha com aquele código, principal antes de secundária |
Mande o par quando o seu CNAE tiver mais de uma linha e você precisar da que não é a padrão. Código fora do catálogo é 422 nfse.service_code_unknown; código que existe mas não é da empresa, 422 nfse.service_code_not_allowed.
service_location_ibgestringMunicípio onde o serviço foi executado: código IBGE de 7 dígitos, apenas números. Opcional na maioria das notas.
• Obrigatório quando o serviço tem o ISS devido no local da execução (LC 116/2003, art. 3º — obras, demolição, limpeza, vigilância, eventos, estacionamento e os demais subitens da regra). Nesses casos o ISS da nota vai para o município informado aqui, e não para o do prestador. Sem o campo, a nota é recusada com 422 nfse.service_location_required. Esta exigência entra junto com a emissão com ISS retido pelo tomador (iss_retido_pelo_tomador); antes dela, o campo só é exigido no caso abaixo.
• Também é exigido quando a atividade da empresa pede o registro do local da execução, mesmo que o ISS continue no município do prestador.
• Código que não existe na tabela do IBGE é 422 nfse.service_location_invalid.
• Nas demais notas o campo é ignorado: o local da prestação é o município do prestador.
A linha da nota (e daí a obrigatoriedade) é a resolvida por cnae e service_code.
iss_retido_pelo_tomadorbooleanO tomador retém o ISS desta nota? Opcional; omitido = não retido.
Marque só quando o tomador é obrigado pela lei do município dele a reter o ISS e pagar direto à prefeitura.
• Com a emissão com retenção habilitada, a nota sai com o ISS retido pelo tomador e, para empresa do Simples Nacional, com a alíquota de ISS da LC 123/2006, art. 21, § 4º, informada no documento. A retenção exige tomador com CNPJ no Brasil e não vale para todo prestador (MEI, por exemplo): quando não vale para a nota, true volta 422 nfse.iss_withholding_not_allowed com o motivo, e a nota não é criada.
• Enquanto ela não estiver habilitada, true volta 422 nfse.iss_retido_indisponivel e a nota não é criada. A Ext emite esta nota para você pelo Emissor Nacional: envie o pedido pelo chat.
retencao_federal_pelo_tomadorbooleanO tomador vai reter impostos federais desta nota (IR e PIS/COFINS/CSLL)? Opcional; omitido = sem retenção. Só para empresa do Lucro Presumido e tomador com CNPJ no Brasil.
• Com a emissão com retenção habilitada, a nota sai com a retenção destacada, calculada pela lei: IR de 1,5% (RIR/2018, art. 714) e PIS/COFINS/CSLL de 4,65% (Lei 10.833/2003, arts. 30 e 31) — a partir da competência de janeiro de 2027, só a CSLL de 1% (LC 214/2025). Retenção de até R$ 10,00 é dispensada pela lei e não vai na nota. Quando a retenção não vale para a nota (empresa fora do Lucro Presumido, tomador sem CNPJ), true volta 422 nfse.federal_withholding_not_allowed com o motivo.
• Enquanto ela não estiver habilitada, true volta 422 nfse.federal_withholding_unavailable e a nota não é criada. A Ext emite esta nota para você pelo Emissor Nacional: envie o pedido pelo chat.
Nota aceita e enfileirada, status queued. Uma repetição atendida pela idempotência devolve este mesmo corpo, com o status que a nota original tiver no momento.
Payload inválido: campo mal formatado, ausente ou fora do domínio aceito. Corrigir e reenviar resolve.
Chave de API ausente, mal formada, revogada ou inexistente. Nenhuma nota é criada.
A chave é válida mas não pode fazer isto: falta o escopo exigido, ou a API pública está desligada para a empresa.
Conflito com uma requisição anterior — mesma Idempotency-Key ainda em voo, mesma Idempotency-Key com outro corpo, reference já usada, ou nota equivalente criada há menos de um minuto. Consulte antes de reenviar.
O corpo da requisição passou do tamanho máximo aceito. Reenviar o mesmo corpo não resolve.
Corpo sem Content-Type: application/json — o servidor não o lê. Envie o header e reenvie.
A requisição está bem formada mas não pode prosseguir: pendência de cadastro da empresa, ou município que exige o tomador. Reenviar sem mudar nada não resolve. (A Idempotency-Key reaproveitada com outro corpo saiu daqui e virou 409: ela é conflito de estado da chave, não corpo improcessável.)
Limite de requisições por chave excedido. Aguarde o que o header Retry-After indicar.
Falha nossa. retryable é true: repita com a MESMA Idempotency-Key para não duplicar a nota, e cite o request_id se persistir.
# valor em CENTAVOS: R$ 1.250,50 = 125050
# reference: o id da fatura no seu sistema — reencontra a nota e recusa a segunda
# cnae: opcional — sem ele a nota sai na atividade principal da empresa
curl -X POST https://api.extcontabilidade.com.br/v1/nfse \
-H "Authorization: Bearer $EXT_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"amount_in_cents": 125050,
"description": "Licenciamento de uso da plataforma — plano mensal",
"customer": { "document": "12345678000190" },
"reference": "INV-2026-0042",
"cnae": "6202300"
}'{
"object": "nfse",
"id": "nfse_9f8e7d6c-5b4a-4321-9876-0a1b2c3d4e5f",
"livemode": true,
"status": "issued",
"amount_in_cents": 125050,
"description": "Desenvolvimento de software sob encomenda — competência 07/2026",
"competence": "2026-01-31",
"customer": {
"document": "12345678000190",
"name": "Maria Souza",
"type": "foreign",
"tax_id": "98-7654321",
"tax_id_absence_reason": "not_required",
"address": {}
},
"foreign_amount": {
"currency": "EUR",
"amount_in_cents": 360000
},
"reference": "INV-2026-0042",
"service_code": "010501",
"cnae": "6202300",
"access_key": "31260712345678000190550010000000011234567890",
"failed_attempts": 0,
"created_at": "2026-07-15T13:42:07.512Z",
"issued_at": "2026-07-15T13:43:19.004Z",
"cancelable_until": "2026-02-08T02:59:59.000Z",
"cancellation": {
"status": "pending",
"requested_at": "2026-08-31T19:00:38.539Z",
"reason": "issuance_error",
"error": {
"code": "nfse.cancel_failed",
"message": "Não recebemos a confirmação do cancelamento. NÃO gere uma nova nota fiscal — acione o suporte para conferir o estado da nota no SEFIN."
}
},
"failed_at": "2026-07-15T13:43:19.004Z",
"error": {
"code": "issue_failed",
"message": "A emissão da nota falhou. Confira os dados enviados; se estiverem corretos, fale com o suporte ANTES de reemitir."
},
"links": {
"pdf": "https://api.extcontabilidade.com.br/v1/nfse/nfse_9f8e7d6c-5b4a-4321-9876-0a1b2c3d4e5f/pdf?t=eyJhbGciOiJIUzI1NiJ9...",
"xml": "https://api.extcontabilidade.com.br/v1/nfse/nfse_9f8e7d6c-5b4a-4321-9876-0a1b2c3d4e5f/xml?t=eyJhbGciOiJIUzI1NiJ9..."
}
}Cancelamento é definitivo, e com ext_sk_live_ a nota deixa de valer como documento fiscal.
Só cancela nota emitida no mês corrente: uma de 30/08 pode ser cancelada até 31/08, e em 01/09 não mais. Leia o instante exato em cancelable_until, não recalcule a regra. Fora da janela, o suporte ainda pode cancelar.
A resposta é 202: acompanhe o objeto cancellation. Ele é pending até o desfecho, succeeded quando o status chega a canceled, e failed quando não recebemos a confirmação — aí nunca reemita, acione o suporte para conferir o estado da nota.
idstringobrigatórioId da nota, com ou sem o prefixo nfse_. O UUID cru funciona; um prefixo de outro tipo de recurso é 400, não 404.
reasonstring: issuance_error | service_not_provided | otherMotivo do cancelamento, registrado no evento fiscal. Omitido, assume other.
descriptionstringJustificativa livre, de 15 a 255 caracteres — o limite é da nota fiscal, não nosso, e fora dele o cancelamento é recusado. Validamos aqui para a recusa chegar na hora, e não minutos depois com a nota presa. Omitida, usamos uma padrão.
Pedido aceito. cancellation.status é pending; consulte a nota até ele virar succeeded ou failed.
Payload inválido: campo mal formatado, ausente ou fora do domínio aceito. Corrigir e reenviar resolve.
Chave de API ausente, mal formada, revogada ou inexistente. Nenhuma nota é criada.
A chave é válida mas não pode fazer isto: falta o escopo exigido, ou a API pública está desligada para a empresa.
Não existe nota com este id para a empresa da chave. Mesmo corpo para id inexistente e para nota de outra empresa.
A nota não está em estado de ser cancelada: já cancelada (nfse.already_canceled), com pedido em andamento (nfse.cancel_in_progress) ou não cancelável por esta API (nfse.not_cancelable). Nenhum dos três se repete.
O corpo da requisição passou do tamanho máximo aceito. Reenviar o mesmo corpo não resolve.
Corpo sem Content-Type: application/json — o servidor não o lê. Envie o header e reenvie.
A nota é de um mês anterior (nfse.cancel_window_closed). Pela API, o cancelamento só vale dentro do mês da emissão.
Limite de requisições por chave excedido. Aguarde o que o header Retry-After indicar.
Falha nossa. retryable é true: repita com a MESMA Idempotency-Key para não duplicar a nota, e cite o request_id se persistir.
curl -X POST 'https://api.extcontabilidade.com.br/v1/nfse/{id}/cancel' \
-H 'Authorization: Bearer ext_sk_...' \
-H 'Content-Type: application/json' \
-d '{
"reason": "issuance_error",
"description": "Valor lançado errado no pedido 4471."
}'{
"object": "nfse",
"id": "nfse_9f8e7d6c-5b4a-4321-9876-0a1b2c3d4e5f",
"livemode": true,
"status": "issued",
"amount_in_cents": 125050,
"description": "Desenvolvimento de software sob encomenda — competência 07/2026",
"competence": "2026-01-31",
"customer": {
"document": "12345678000190",
"name": "Maria Souza",
"type": "foreign",
"tax_id": "98-7654321",
"tax_id_absence_reason": "not_required",
"address": {}
},
"foreign_amount": {
"currency": "EUR",
"amount_in_cents": 360000
},
"reference": "INV-2026-0042",
"service_code": "010501",
"cnae": "6202300",
"access_key": "31260712345678000190550010000000011234567890",
"failed_attempts": 0,
"created_at": "2026-07-15T13:42:07.512Z",
"issued_at": "2026-07-15T13:43:19.004Z",
"cancelable_until": "2026-02-08T02:59:59.000Z",
"cancellation": {
"status": "pending",
"requested_at": "2026-08-31T19:00:38.539Z",
"reason": "issuance_error",
"error": {
"code": "nfse.cancel_failed",
"message": "Não recebemos a confirmação do cancelamento. NÃO gere uma nova nota fiscal — acione o suporte para conferir o estado da nota no SEFIN."
}
},
"failed_at": "2026-07-15T13:43:19.004Z",
"error": {
"code": "issue_failed",
"message": "A emissão da nota falhou. Confira os dados enviados; se estiverem corretos, fale com o suporte ANTES de reemitir."
},
"links": {
"pdf": "https://api.extcontabilidade.com.br/v1/nfse/nfse_9f8e7d6c-5b4a-4321-9876-0a1b2c3d4e5f/pdf?t=eyJhbGciOiJIUzI1NiJ9...",
"xml": "https://api.extcontabilidade.com.br/v1/nfse/nfse_9f8e7d6c-5b4a-4321-9876-0a1b2c3d4e5f/xml?t=eyJhbGciOiJIUzI1NiJ9..."
}
}Destino do Location do 202. Consulte até o status chegar a issued ou failed, respeitando o Retry-After.
Escopada pelo MODO da chave: a de teste enxerga só notas de teste, e a de produção só notas reais. O que não é do modo responde 404, igual a um id inexistente.
idstringobrigatórioId da nota, com ou sem o prefixo nfse_. O UUID cru funciona; um prefixo de outro tipo de recurso é 400, não 404.
Estado atual da nota.
Payload inválido: campo mal formatado, ausente ou fora do domínio aceito. Corrigir e reenviar resolve.
Chave de API ausente, mal formada, revogada ou inexistente. Nenhuma nota é criada.
A chave é válida mas não pode fazer isto: falta o escopo exigido, ou a API pública está desligada para a empresa.
Não existe nota com este id para a empresa da chave. Mesmo corpo para id inexistente e para nota de outra empresa.
Limite de requisições por chave excedido. Aguarde o que o header Retry-After indicar.
Falha nossa. retryable é true: repita com a MESMA Idempotency-Key para não duplicar a nota, e cite o request_id se persistir.
curl 'https://api.extcontabilidade.com.br/v1/nfse/{id}' \
-H 'Authorization: Bearer ext_sk_...'{
"object": "nfse",
"id": "nfse_9f8e7d6c-5b4a-4321-9876-0a1b2c3d4e5f",
"livemode": true,
"status": "issued",
"amount_in_cents": 125050,
"description": "Desenvolvimento de software sob encomenda — competência 07/2026",
"competence": "2026-01-31",
"customer": {
"document": "12345678000190",
"name": "Maria Souza",
"type": "foreign",
"tax_id": "98-7654321",
"tax_id_absence_reason": "not_required",
"address": {}
},
"foreign_amount": {
"currency": "EUR",
"amount_in_cents": 360000
},
"reference": "INV-2026-0042",
"service_code": "010501",
"cnae": "6202300",
"access_key": "31260712345678000190550010000000011234567890",
"failed_attempts": 0,
"created_at": "2026-07-15T13:42:07.512Z",
"issued_at": "2026-07-15T13:43:19.004Z",
"cancelable_until": "2026-02-08T02:59:59.000Z",
"cancellation": {
"status": "pending",
"requested_at": "2026-08-31T19:00:38.539Z",
"reason": "issuance_error",
"error": {
"code": "nfse.cancel_failed",
"message": "Não recebemos a confirmação do cancelamento. NÃO gere uma nova nota fiscal — acione o suporte para conferir o estado da nota no SEFIN."
}
},
"failed_at": "2026-07-15T13:43:19.004Z",
"error": {
"code": "issue_failed",
"message": "A emissão da nota falhou. Confira os dados enviados; se estiverem corretos, fale com o suporte ANTES de reemitir."
},
"links": {
"pdf": "https://api.extcontabilidade.com.br/v1/nfse/nfse_9f8e7d6c-5b4a-4321-9876-0a1b2c3d4e5f/pdf?t=eyJhbGciOiJIUzI1NiJ9...",
"xml": "https://api.extcontabilidade.com.br/v1/nfse/nfse_9f8e7d6c-5b4a-4321-9876-0a1b2c3d4e5f/xml?t=eyJhbGciOiJIUzI1NiJ9..."
}
}Devolve os BYTES do PDF no corpo, nunca um redirect para URL assinada. Aceita a chave de API ou o token temporário que o links.pdf da consulta já traz na query: ele vale 15 minutos, serve só esta nota, e é o que faz o link abrir para quem não tem a chave. Só existe depois de issued; antes disso, 409 nfse.not_issued. Vale nos dois modos — em livemode: false o PDF vem carimbado sem validade jurídica.
idstringobrigatórioId da nota, com ou sem o prefixo nfse_. O UUID cru funciona; um prefixo de outro tipo de recurso é 400, não 404.
PDF da nota. Content-Disposition: attachment, com nome derivado do id.
Payload inválido: campo mal formatado, ausente ou fora do domínio aceito. Corrigir e reenviar resolve.
Chave de API ausente, mal formada, revogada ou inexistente. Nenhuma nota é criada.
A chave é válida mas não pode fazer isto: falta o escopo exigido, ou a API pública está desligada para a empresa.
Não existe nota com este id para a empresa da chave.
A nota existe, mas ainda não foi autorizada (nfse.not_issued). Espere o Retry-After e consulte o status antes de tentar de novo.
Limite de requisições por chave excedido. Aguarde o que o header Retry-After indicar.
Falha nossa. retryable é true: repita com a MESMA Idempotency-Key para não duplicar a nota, e cite o request_id se persistir.
curl 'https://api.extcontabilidade.com.br/v1/nfse/{id}/pdf' \
-H 'Authorization: Bearer ext_sk_...'O XML autorizado — é ele o documento fiscal; o PDF é a representação impressa. Mesma forma do PDF: só depois de issued, e vale nos dois modos. Em livemode: false ele traz tpAmb: 2, a marca de que a nota não tem validade fiscal.
idstringobrigatórioId da nota, com ou sem o prefixo nfse_. O UUID cru funciona; um prefixo de outro tipo de recurso é 400, não 404.
XML autorizado, sem reprocessamento. Content-Disposition: attachment, com nome derivado do id.
Payload inválido: campo mal formatado, ausente ou fora do domínio aceito. Corrigir e reenviar resolve.
Chave de API ausente, mal formada, revogada ou inexistente. Nenhuma nota é criada.
A chave é válida mas não pode fazer isto: falta o escopo exigido, ou a API pública está desligada para a empresa.
Não existe nota com este id para a empresa da chave.
A nota existe mas ainda não foi autorizada (nfse.not_issued) — não há XML para baixar.
Limite de requisições por chave excedido. Aguarde o que o header Retry-After indicar.
Falha nossa. retryable é true: repita com a MESMA Idempotency-Key para não duplicar a nota, e cite o request_id se persistir.
curl 'https://api.extcontabilidade.com.br/v1/nfse/{id}/xml' \
-H 'Authorization: Bearer ext_sk_...'Os valores de cnae e service_code que o POST /v1/nfse aceita desta empresa — e só eles: toda linha desta lista é emitível.
Use is_default para saber se o cnae sozinho basta: sendo false, mande o par cnae + service_code para chegar naquela linha. A nota que não manda nenhum dos dois sai na linha que tem is_main e is_default ao mesmo tempo.
Uma atividade do seu cadastro na Receita Federal pode não aparecer aqui. Isso não significa que o seu cadastro esteja errado: a atividade ainda não está liberada para emissão, quase sempre por pendência de cadastro do nosso lado. Fale com o suporte.
Sem filtro por query string e sem paginação — a lista inteira vem numa resposta só.
Envelope de lista. data vem vazio quando nenhuma atividade da empresa está liberada para emissão — resposta legítima, não erro.
Chave de API ausente, mal formada, revogada ou inexistente. Nenhuma nota é criada.
A chave é válida mas não pode fazer isto: falta o escopo exigido, ou a API pública está desligada para a empresa.
Limite de requisições por chave excedido. Aguarde o que o header Retry-After indicar.
Falha nossa. retryable é true: repita com a MESMA Idempotency-Key para não duplicar a nota, e cite o request_id se persistir.
curl 'https://api.extcontabilidade.com.br/v1/activities' \
-H 'Authorization: Bearer ext_sk_...'{
"object": "list",
"has_more": false,
"data": [
{
"cnae": null,
"service_code": null,
"description": null,
"is_main": null,
"is_default": null
}
]
}