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。
状态机
┌──────────────► Negada(终态)│Pendente ────────┼──────────────► Autorizada ──┬──► Cancelada(终态)│ │└──────────────► Falha ├──► EmProcessoDeCancelamento ──┬──► Cancelada(取号前失败,无单据) │ └──► Autorizada(分析被驳回)└──► Substituida(被替代票取代)
| 状态 | 含义 | 是否终态 |
|---|---|---|
Pendente | 已受理,尚无结论 | 否 |
Autorizada | 发行成功,票已生效 | 否(可取消 / 被替代) |
Negada | 发行方驳回 | 是 |
Falha | 平台侧失败(参数错误 / 主体不可开票),没有产生单据 | 是 |
Cancelada | 已取消 | 是 |
EmProcessoDeCancelamento | 取消分析申请已受理,等市政裁决 | 否 |
Substituida | 已被替代开票取代 | 是 |
进项票的表态状态
进项票的 status 沿用上表的 Autorizada / Cancelada / Substituida;本主体对它的表态另记在 manifestacao 块,一张票每方只能表态一次。
(未表态,列表过滤值 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 | 本票被替代票取代;载荷多一个 nfseChaveSubstituta | Substituida |
nfse.inbound.received | 进项 NFS-e 到达(本主体为接收方 / 中介方,需显式订阅) | — |
nfse.inbound.canceled | 进项 NFS-e 被取消(需显式订阅) | — |
nfse.inbound.substituted | 进项 NFS-e 被替代(需显式订阅) | — |
注意: 订阅语义。应用从未登记过任何订阅行时,销项六个事件全推;一旦为应用登记了任何一条订阅(例如订阅进项事件),该应用就进入白名单模式,只推显式开启的事件。订阅进项事件时请把需要的销项事件一并开启,否则销项通知会停在「暂存待认领」。
{"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 对账。
错误模型
对外端点返回无包装的数组:
[{"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 |
10018022 | Simples 主体不得直传 ISS 税率 | 去掉 aliquota |
10018023 | 判定联邦源泉扣缴还缺信息,消息一次列出全部缺项 | 补齐所列字段,或在 servico.retencoes 直传扣缴额 |
10018030 | 同一请求正在处理中 | 稍后重试 |
10018031 | 该单号已开票 / 同 id 不同内容 | 换单号或核对内容 |
10018040 | 取消分析申请已在处理中 | 等裁决 |
10018041 | 该票已被替代 | 不可再操作 |
10018042 | 当前状态不允许该事件 | 核对票状态 |
10018063 | DPS 报文不符合官方 XSD(明细含行号、列号、错误键与元素名) | 按明细修正:常见原因是文本含 Latin-1 以外字符、地址不完整、码值不在官方码表 |
10018064 | NFS-e 事件报文不符合官方 XSD | 核对 motivo 长度(15-255)、codigoMotivo 取值与字符集 |
10018065 | 本票需要的 NFS-e 计税基础数据尚未导入,受理时即拒;消息按「业务项 -> 可改传的请求字段」列出 | 在请求里直接传箭头后的字段,或联系平台确认数据已导入;iss-local-incidencia 传 servico.iss.municipioIncidencia 即可 |
鉴权与授权错误(10009xxx)见认证与签名,完整错误码表见错误码。
联调清单
- 注册主体(带
emissaoNFSe)→ 关联证书 → 登记 Webhook。 - 用
Homologacao开一张最小票(只填必填项),确认收到nfse.authorized且能下载 XML 与 DANFSe。 - 用同一个
id再提交一次,确认没有开出第二张票。 - 故意传一个不存在的服务码,确认收到
10018012的同步拒绝,而不是异步Negada。 - 对已授权的票调用取消,确认按窗口走到
Cancelada或EmProcessoDeCancelamento。 - 查询一张发行失败的票,确认能用
identificacaoDps而不是chaveAcesso对上账。 - 对一张进项票调表态传
tipo=Confirmacao,任务成功后查详情确认manifestacao.status=Confirmada;再对同一票传tipo=Rejeicao,确认收到10018042(每方只能表态一次)。
