TF Fiscal
Documentação

NFS-e

Emita notas fiscais de serviço (NFS-e) em nome de um emitente registrado: fluxo de integração, diferenças entre canais municipais, manifestação de notas recebidas, cargas dos webhooks, modelo de erros, máquina de estados e checklist de integração.

Visão geral e fluxo de integração

PassoEndpointObservações
1 Registrar emitentePOST /openapi/v2/empresasIgual à NF-e, com a nova seção opcional emissaoNFSe; o bloco de NF-e pode ser omitido se você só emite NFS-e, e a IE não é exigida nesse caso
2 Vincular certificadoPOST /openapi/v1/empresas/{empresaId}/certificadoDigitalIgual à NF-e
3 Registrar callbackPOST /openapi/v1/webhooksIgual à NF-e; os resultados de NFS-e reutilizam a mesma URL de callback
4 EmitirPOST /openapi/v2/empresas/{empresaId}/nfseRetorna HTTP 200 sem corpo na aceitação; o envio é assíncrono
5 ConsultarGET /openapi/v2/empresas/{empresaId}/nfse/{nfseId}Status, eventos, links de download do XML / DANFSe, eco da requisição
5-A ConciliarGET …/nfse/emitidas · GET …/nfse/emitidas/{chaveAcesso}Lista de emitidas (paginação por janela de tempo) e consulta por chave de acesso
6 CancelarPOST /openapi/v2/empresas/{empresaId}/nfse/{nfseId}/cancelarAssíncrono: 200 com recibo de aceitação, resultado via webhook
7 Consultar notas recebidasGET …/nfse/recebidas · GET …/nfse/recebidas/{chaveAcesso}Lista das notas recebidas (paginada por período) e consulta de uma pela chave; é daqui que sai a chaveAcesso usada na manifestação
8 Manifestar sobre nota recebidaPOST …/recebidas/{chaveAcesso}/manifestarRegistra confirmação / rejeição de uma NFS-e recebida; o corpo traz tipo

A autenticação é idêntica à da NF-e: cabeçalhos token / timestamp / sign, veja Autenticação.

Três diferenças estruturais em relação à NF-e

1. A chave de acesso tem 50 dígitos e é gerada pelo emissor oficial. A chave de 44 dígitos da NF-e é calculada pelo contribuinte; a chave da NFS-e só existe depois que a autoridade aceita o documento. Um documento que falhou, portanto, não tem chaveAcesso e só pode ser localizado por identificacaoDps, o identificador de DPS de 45 caracteres que geramos.

2. A nota de serviço é regida pelo município. A plataforma roteia pelo município do emitente; o canal resultante aparece como padraoMunicipal na resposta da consulta. Hoje há suporte ao padrão nacional (Nacional) e ao sistema municipal de São Paulo (Paulistana); um município não suportado é rejeitado na aceitação (10018010) em vez de aceito e falhado de forma assíncrona.

3. O cancelamento tem janela, e fora dela a resposta não é "não dá para cancelar" e sim "vai para análise municipal". Veja Cancelar NFS-e.

Pré-requisitos

  • O emitente precisa estar aprovado e com certificado utilizável, caso contrário a aceitação é rejeitada (10018004).
  • O emitente precisa ter perfil de NFS-e, criado pela seção emissaoNFSe de Registrar empresa ou mantido pela operação no console. Enquadramento no Simples, apuração no Simples, regime especial e série do DPS vêm sempre do perfil; a requisição de emissão não os envia nem os substitui. Sem perfil, a aceitação recusa (10018005). Código de serviço, CNAE, NBS e os demais campos do serviço não ficam no perfil: vão em cada nota.
  • O ambienteEmissao precisa coincidir com o ambiente atual do emitente. Após o registro o emitente fica no ambiente de testes (Homologacao); a virada para produção é ação da operação e não é exposta pela API.
  • Os valores de tributação seguem a requisição: o que ela traz prevalece, e só o que falta a plataforma busca nos dados de referência fiscal, como a lista da LC 116, a classificação e as alíquotas de retenção federal e a carga aproximada do IBPT. Se os dados que a retenção federal, as alíquotas próprias de PIS/COFINS ou o local de incidência do ISS precisam consultar ainda não foram importados, a aceitação recusa com 10018065, listando os campos da requisição que podem substituí-los.
  • Quando o tomador é pessoa jurídica (CNPJ) há retenção federal: envie os valores em servico.retencoes, em que zero declara que não há retenção, ou omita-os junto com pisCofins.tipoRetencao e a plataforma os determina pela classificação oficial de retenção do serviço e por cliente.orgaoPublico e cliente.optanteSimplesNacional; enviar pisCofins.tipoRetencao sem os valores é recusado (10018001). Informação faltante é rejeitada explicitamente (10018023) com tudo o que falta listado de uma vez, em vez de tratada como zero.

Restrições adicionais do canal Paulistana

Um emitente de São Paulo (padraoMunicipal = Paulistana) precisa do CCM (inscricaoMunicipal): se estiver no perfil, é usado como está; se faltar, a plataforma consulta o sistema municipal por CNPJ na aceitação e grava o resultado no perfil; a aceitação só é rejeitada (10018005) quando nada é encontrado.

O canal não suporta campos de desconto: incorpore o desconto ao valor do serviço ou à dedução. Também não suporta emissão substitutiva (o layout municipal de RPS não tem esse grupo): cancele a original e emita uma nova; enviar o grupo é rejeitado na aceitação (10018001).

Ele também ganha duas capacidades automáticas que não exigem nenhum endpoint novo: as notas paulistanas recebidas são puxadas direto do sistema municipal para a lista de recebidas (origem=PrefeituraSP, mescladas automaticamente quando a transcrição da ADN chega); uma nota emitida e cancelada no portal municipal é sincronizada para Cancelada e o nfse.canceled é enviado normalmente.

Máquina de estados

text
┌──────────────► Negada (terminal)
│
Pendente ────────┼──────────────► Autorizada ──┬──► Cancelada (terminal)
│ │
└──────────────► Falha ├──► EmProcessoDeCancelamento ──┬──► Cancelada
(falhou antes da numeração, │ └──► Autorizada (análise indeferida)
nenhum documento existe) └──► Substituida (substituída)
StatusSignificadoTerminal
PendenteAceito, sem desfecho aindanão
AutorizadaEmitida com sucessonão (pode ser cancelada ou substituída)
NegadaRejeitada pela autoridadesim
FalhaFalha do lado da plataforma (parâmetros inválidos / emitente inapto); nenhum documento foi criadosim
CanceladaCanceladasim
EmProcessoDeCancelamentoAnálise de cancelamento aceita, aguardando o municípionão
SubstituidaSubstituída por outro documentosim

Status de manifestação de uma nota recebida

O status de uma nota recebida usa Autorizada / Cancelada / Substituida da tabela acima; a manifestação deste emitente fica separada no bloco manifestacao, e cada parte manifesta no máximo uma vez por nota.

text
(sem manifestação, filtro da lista Pendente) ──┬──► Confirmada (confirmada por este emitente, terminal)
├──► Rejeitada (rejeitada por este emitente) ──► RejeicaoAnulada (rejeição anulada pelo município, terminal)
└──► ConfirmacaoTacita (confirmação tácita do município, terminal)
StatusSignificadoAinda pode manifestar
null (valor de filtro Pendente)Sem manifestaçãosim (enquanto a nota estiver Autorizada)
ConfirmadaConfirmada por este emitentenão
RejeitadaRejeitada por este emitentenão
ConfirmacaoTacitaConfirmada tacitamente pelo município após o prazonão
RejeicaoAnuladaO município anulou a rejeição deste emitentenão

Callbacks de webhook

Entregues na URL que você registra como carga JSON pura.

Código do eventoDisparonfseStatus
nfse.authorizedEmitida com sucessoAutorizada
nfse.rejectedRejeitada pela autoridade ou falha do nosso ladoNegada
nfse.canceledCancelamento concluído (inclusive análise deferida)Cancelada
nfse.cancel_requestedAnálise de cancelamento aceitaEmProcessoDeCancelamento
nfse.cancel_deniedO cancelamento não ocorreu e o documento segue autorizado: recusa síncrona da autoridade, envio esgotado, ou pedido de análise indeferido pelo municípioAutorizada
nfse.substitutedEste documento foi substituído; a carga traz um nfseChaveSubstituta adicionalSubstituida
nfse.inbound.receivedNFS-e recebida (este emitente é tomador / intermediário, exige assinatura explícita)—
nfse.inbound.canceledNFS-e recebida foi cancelada (exige assinatura explícita)—
nfse.inbound.substitutedNFS-e recebida foi substituída (exige assinatura explícita)—

Nota: Semântica das assinaturas. Um aplicativo que nunca registrou nenhuma linha de assinatura recebe todos os seis eventos de saída. Assim que existir qualquer linha de assinatura para o aplicativo (por exemplo um evento de entrada), ele passa ao modo lista de permissão e só os eventos explicitamente habilitados são entregues. Ao assinar eventos de entrada, habilite também os eventos de saída necessários, senão as notificações de saída ficam paradas aguardando destinatário.

json
{
"tipo": "NFS-e",
"empresaId": "1001",
"nfseId": "PED-20260918-001",
"nfseStatus": "Autorizada",
"nfseMotivoStatus": null,
"nfseLinkDanfse": "https://api.v2.tffiscal.com/openapi/files/danfse/…",
"nfseLinkXml": "https://api.v2.tffiscal.com/openapi/files/xml/9001?token=…",
"nfseNumero": "1287",
"nfseSerie": "1",
"nfseChaveAcesso": "3550308…",
"nfseIdentificacaoDps": "DPS3550308…",
"nfseDataEmissao": "2026-09-18T13:00:00Z",
"nfseDataAutorizacao": "2026-09-18T13:00:03Z",
"nfseNumeroProtocolo": null,
"nfseAtualizadoEm": "2026-09-18T13:00:03Z"
}

nfseAtualizadoEm é o momento desta mudança de estado. A ordem de entrega não é garantida: retentativas e múltiplas instâncias podem fazer um evento antigo chegar depois de um novo. Compare com o horário que você já tem e descarte o mais antigo em vez de sobrescrever o estado pela ordem de chegada.

Num callback de falha o nfseChaveAcesso pode vir vazio (a autoridade nunca gerou a chave); concilie por nfseIdentificacaoDps nesse caso. Um callback de falha anterior à numeração não traz nenhum dos dois, e só o nfseId concilia.

Modelo de erros

Os endpoints públicos retornam um array puro:

json
[{"codigo": "10018012", "mensagem": "Codigo de servico fora da lista oficial LC 116: 999999"}]
CódigoSignificadoO que fazer
10018001Validação de parâmetros recusada na aceitação (a mensagem indica o motivo)Corrija conforme a mensagem
NFSe0001Documento não encontrado: o nfseId não pertence a este emitente, ou a chave de entrada não está entre as notas recebidas deleConfira nfseId / chaveAcesso contra empresaId
10018004Emitente inapto (desativado / certificado inutilizável)Verifique o status do emitente e o certificado
10018005Emitente sem perfil de NFS-eCrie-o antes em Registrar empresa com a seção emissaoNFSe
10018010O padrão de NFS-e do município ainda não é suportadoEsse município ainda não pode emitir, fale com a plataforma
10018012Código de serviço fora da lista LC 116Confira codigoTributacaoNacional
10018013Documento do tomador inválidoConfira o CPF / CNPJ / NIF
10018014Exportação de serviço sem país de destinoInforme servico.paisPrestacao
10018015O documento substituído não pode ser substituídoConfira o status do documento substituído
10018020Não foi possível determinar a alíquota de ISSEnvie servico.iss.aliquota ou o servico.codigoTributacaoMunicipal correto
10018022Emitente do Simples não pode enviar a alíquota de ISS diretamenteRemova aliquota
10018023A retenção federal não pode ser determinada; a mensagem lista tudo o que faltaInforme os campos listados ou envie os valores retidos em servico.retencoes
10018030A mesma requisição já está em processamentoTente de novo mais tarde
10018031Esse pedido já tem nota, ou o mesmo id traz conteúdo diferenteUse outro número de pedido ou confira o conteúdo
10018040Já há uma análise de cancelamento em andamentoAguarde o veredito
10018041O documento já foi substituídoNenhuma outra operação é permitida
10018042O status atual não permite este eventoConfira o status do documento
10018063A mensagem do DPS não atende ao XSD oficial (o detalhe traz linha, coluna, chave do erro e nome do elemento)Corrija conforme o detalhe: em geral texto fora do Latin-1, endereço incompleto, ou valor fora da tabela oficial
10018064A mensagem de evento da NFS-e não atende ao XSD oficialConfira o tamanho de motivo (15-255), o valor de codigoMotivo e o conjunto de caracteres
10018065Os dados de referência fiscal da NFS-e de que esta nota precisa não foram importados, recusa na aceitação; a mensagem lista cada item com uma seta para o campo da requisição que o substituiEnvie na requisição os campos após a seta, ou confirme com a plataforma que os dados foram importados; para iss-local-incidencia, basta enviar servico.iss.municipioIncidencia

Erros de autenticação e autorização (10009xxx) estão em Autenticação; a tabela completa está em Códigos de erro.

Checklist de integração

  1. Registre o emitente (com emissaoNFSe) → vincule o certificado → registre o webhook.
  2. Emita uma nota mínima em Homologacao (só os campos obrigatórios), confirme que o nfse.authorized chega e que o XML e o DANFSe baixam.
  3. Envie o mesmo id outra vez e confirme que nenhuma segunda nota é emitida.
  4. Envie um código de serviço inexistente e confirme a recusa síncrona 10018012, e não uma Negada assíncrona.
  5. Cancele uma nota autorizada e confirme que ela chega a Cancelada ou EmProcessoDeCancelamento conforme a janela.
  6. Consulte um documento que falhou e confirme que ele concilia por identificacaoDps, não por chaveAcesso.
  7. Manifeste sobre uma nota recebida com tipo=Confirmacao e veja o detalhe trazer manifestacao.status=Confirmada; envie tipo=Rejeicao na mesma nota e confirme o 10018042 (cada parte manifesta uma vez).