TF Fiscal
开发文档

NFS-e

代已注册主体开具服务发票 NFS-e:接入流程、市政通道差异、进项票表态、Webhook 载荷、错误模型、状态机与联调清单。

能力概述与接入流程

步骤接口说明
1 注册主体POST /openapi/v2/empresas与 NF-e 相同,新增可选 emissaoNFSe 段;只做 NFS-e 可不传 NF-e 块,也不要求州注册号
2 关联证书POST /openapi/v1/empresas/{empresaId}/certificadoDigital与 NF-e 相同
3 登记回调POST /openapi/v1/webhooks与 NF-e 相同;NFS-e 结果复用同一回调地址
4 发行POST /openapi/v2/empresas/{empresaId}/nfse受理即返回 200 无内容,异步向发行方申报
5 查询GET /openapi/v2/empresas/{empresaId}/nfse/{nfseId}状态、事件、XML / DANFSe 下载链接、入参回显
5-A 对账GET …/nfse/emitidas · GET …/nfse/emitidas/{chaveAcesso}销项清单(时间窗分页)与按密钥查一张
6 取消POST /openapi/v2/empresas/{empresaId}/nfse/{nfseId}/cancelar异步:200 回受理回执,结果经 Webhook
7 进项查询GET …/nfse/recebidas · GET …/nfse/recebidas/{chaveAcesso}收到的票清单(时间窗分页)与按密钥查一张;表态所需的 chaveAcesso 从这里拿
8 进项票表态POST …/recebidas/{chaveAcesso}/manifestar对收到的 NFS-e 登记确认 / 拒绝,请求体带 tipo

鉴权与 NF-e 完全相同:请求头 token / timestamp / sign,见认证与签名。

与 NF-e 的三点结构差异

1. 访问密钥是 50 位,且由发行方生成。 NF-e 的 44 位密钥由开票方自己算,NFS-e 的 50 位密钥要等发行方受理后才有。所以发行失败的单据没有 chaveAcesso,只能用 identificacaoDps(本方生成的 45 位 DPS 标识)定位。

2. 服务发票由市镇管辖。 平台按开票方所在市镇路由到对应通道,路由结果在查询响应的 padraoMunicipal 里。当前已支持国家标准(Nacional)与圣保罗市自有系统(Paulistana),未支持的市镇在受理时就会被拒(10018010),不会先受理再异步失败。

3. 取消有窗口,窗口外不是「不能取消」而是「走市政分析」。 见取消 NFS-e。

前提

  • 主体须已审批通过、证书可用,否则受理即拒(10018004)。
  • 主体须有 NFS-e 档案,由注册主体的 emissaoNFSe 段建立或由运营在控制台维护。Simples 归属、Simples 计税口径、特殊税制与 DPS 序列号永远取档案,开票请求不能传也不能覆盖;没有档案时受理即拒(10018005)。服务码、CNAE、NBS 等服务级字段不在档案里,随每张票传。
  • 请求里的 ambienteEmissao 必须与主体当前环境一致,否则受理即拒。注册后主体处于认证测试环境(Homologacao),切生产是运营动作、不开 API。
  • 计税取值以请求为准:请求给了就用请求的值,没给的才由平台查计税基础数据,如 LC 116 服务清单、联邦扣缴分类与税率、IBPT 近似税负。联邦扣缴、自身 PIS/COFINS 税率或 ISS 计税地要查的数据尚未导入时受理即拒 10018065,消息列出可改传的请求字段。
  • 接收方是法人(CNPJ)时涉及联邦源泉扣缴:可在 servico.retencoes 直传扣缴额,0 表示声明无扣缴;金额与 pisCofins.tipoRetencao 都不传时由平台按服务的官方扣缴分类与接收方的 cliente.orgaoPublico、cliente.optanteSimplesNacional 自行判定;只传 pisCofins.tipoRetencao 不传金额会被拒(10018001)。判定所需信息不齐会显式拒绝(10018023)并一次列出全部缺项,而不是按 0 计。

圣保罗通道的额外约束

圣保罗主体(padraoMunicipal 为 Paulistana)须有市政登记号 CCM(inscricaoMunicipal):档案里登记了就直接用;没登记时平台在受理期自动按 CNPJ 向市政系统查询并回填档案,只有查不到才受理即拒(10018005)。

该通道不支持折扣字段,请把折扣折进服务金额或扣减额;也不支持替代开票(市政 RPS 布局没有该组),请先取消原票再开新票,传了受理即拒(10018001)。

另有两项自动能力,不需要调用任何新接口:收到的圣保罗票由平台直连市政系统同步进进项列表(origem=PrefeituraSP,ADN 转录到达后自动合并);在市政门户取消的销项票自动同步为 Cancelada,照常发 nfse.canceled。

状态机

text
┌──────────────► Negada(终态)
│
Pendente ────────┼──────────────► Autorizada ──┬──► Cancelada(终态)
│ │
└──────────────► Falha ├──► EmProcessoDeCancelamento ──┬──► Cancelada
(取号前失败,无单据) │ └──► Autorizada(分析被驳回)
└──► Substituida(被替代票取代)
状态含义是否终态
Pendente已受理,尚无结论否
Autorizada发行成功,票已生效否(可取消 / 被替代)
Negada发行方驳回是
Falha平台侧失败(参数错误 / 主体不可开票),没有产生单据是
Cancelada已取消是
EmProcessoDeCancelamento取消分析申请已受理,等市政裁决否
Substituida已被替代开票取代是

进项票的表态状态

进项票的 status 沿用上表的 Autorizada / Cancelada / Substituida;本主体对它的表态另记在 manifestacao 块,一张票每方只能表态一次。

text
(未表态,列表过滤值 Pendente)──┬──► Confirmada(本方确认,终态)
├──► Rejeitada(本方拒绝)──► RejeicaoAnulada(市镇作废拒绝,终态)
└──► ConfirmacaoTacita(市镇默示确认,终态)
状态含义还能表态
null(过滤值 Pendente)未表态是(票为 Autorizada 时)
Confirmada本方已确认否
Rejeitada本方已拒绝否
ConfirmacaoTacita市镇按期限默示确认否
RejeicaoAnulada市镇作废了本方的拒绝否

Webhook 回调

在登记 Webhook 的地址上收到,载荷为无包装的 JSON 对象。

事件码触发nfseStatus
nfse.authorized发行成功Autorizada
nfse.rejected被驳回或平台侧失败Negada
nfse.canceled取消完成(含分析准予)Cancelada
nfse.cancel_requested取消分析申请已受理EmProcessoDeCancelamento
nfse.cancel_denied取消未成功,票仍已授权:发行方同步拒绝、出站耗尽、或分析申请被市镇驳回Autorizada
nfse.substituted本票被替代票取代;载荷多一个 nfseChaveSubstitutaSubstituida
nfse.inbound.received进项 NFS-e 到达(本主体为接收方 / 中介方,需显式订阅)—
nfse.inbound.canceled进项 NFS-e 被取消(需显式订阅)—
nfse.inbound.substituted进项 NFS-e 被替代(需显式订阅)—

注意: 订阅语义。应用从未登记过任何订阅行时,销项六个事件全推;一旦为应用登记了任何一条订阅(例如订阅进项事件),该应用就进入白名单模式,只推显式开启的事件。订阅进项事件时请把需要的销项事件一并开启,否则销项通知会停在「暂存待认领」。

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 是本次状态变更的时刻。投递不保证顺序:重试、多实例都可能让旧事件晚于新事件到达。请把它与本地已知时刻比较,旧的直接丢弃,不要按到达顺序覆盖状态。

失败回调里 nfseChaveAcesso 可能为空(发行方从未生成密钥),此时用 nfseIdentificacaoDps 对账。取号前失败的回调连 nfseIdentificacaoDps 都为空,只能靠 nfseId 对账。

错误模型

对外端点返回无包装的数组:

json
[{"codigo": "10018012", "mensagem": "Codigo de servico fora da lista oficial LC 116: 999999"}]
码含义处置
10018001受理期参数校验拒绝(消息说明具体原因)按消息修正
NFSe0001单据不存在:nfseId 不属于本主体,或进项密钥不在本主体的进项里核对 nfseId / chaveAcesso 与 empresaId
10018004主体不可开票(停用 / 证书不可用)检查主体状态与证书
10018005主体缺 NFS-e 档案先用注册主体带 emissaoNFSe 段建档案
10018010市镇的 NFS-e 标准尚未支持该市镇暂不可开,联系平台
10018012服务码不在 LC 116 清单核对 codigoTributacaoNacional
10018013接收方证件不合法核对 CPF / CNPJ / NIF
10018014服务出口缺出境国补 servico.paisPrestacao
10018015被替代票不可替代核对被替代票状态
10018020无法确定 ISS 税率传 servico.iss.aliquota 或正确的 servico.codigoTributacaoMunicipal
10018022Simples 主体不得直传 ISS 税率去掉 aliquota
10018023判定联邦源泉扣缴还缺信息,消息一次列出全部缺项补齐所列字段,或在 servico.retencoes 直传扣缴额
10018030同一请求正在处理中稍后重试
10018031该单号已开票 / 同 id 不同内容换单号或核对内容
10018040取消分析申请已在处理中等裁决
10018041该票已被替代不可再操作
10018042当前状态不允许该事件核对票状态
10018063DPS 报文不符合官方 XSD(明细含行号、列号、错误键与元素名)按明细修正:常见原因是文本含 Latin-1 以外字符、地址不完整、码值不在官方码表
10018064NFS-e 事件报文不符合官方 XSD核对 motivo 长度(15-255)、codigoMotivo 取值与字符集
10018065本票需要的 NFS-e 计税基础数据尚未导入,受理时即拒;消息按「业务项 -> 可改传的请求字段」列出在请求里直接传箭头后的字段,或联系平台确认数据已导入;iss-local-incidencia 传 servico.iss.municipioIncidencia 即可

鉴权与授权错误(10009xxx)见认证与签名,完整错误码表见错误码。

联调清单

  1. 注册主体(带 emissaoNFSe)→ 关联证书 → 登记 Webhook。
  2. 用 Homologacao 开一张最小票(只填必填项),确认收到 nfse.authorized 且能下载 XML 与 DANFSe。
  3. 用同一个 id 再提交一次,确认没有开出第二张票。
  4. 故意传一个不存在的服务码,确认收到 10018012 的同步拒绝,而不是异步 Negada。
  5. 对已授权的票调用取消,确认按窗口走到 Cancelada 或 EmProcessoDeCancelamento。
  6. 查询一张发行失败的票,确认能用 identificacaoDps 而不是 chaveAcesso 对上账。
  7. 对一张进项票调表态传 tipo=Confirmacao,任务成功后查详情确认 manifestacao.status=Confirmada;再对同一票传 tipo=Rejeicao,确认收到 10018042(每方只能表态一次)。