发票邮件
把已授权的 NF-e / NFS-e / CT-e / DC-e 以邮件发给买方:模板与变量、正文 HTML 与图片、附件与下载链接、发件人规则、状态流转、幂等与 409、回调事件、错误模型与联调清单。
能力概述与接入流程
| 步骤 | 接口 | 说明 |
|---|---|---|
| 1 保存模板 | POST /openapi/v2/emails/templates | 租户级,所有主体共用;同一发票类型可有多份,padrao 标默认 |
| 2 登记回调 | POST /openapi/v1/webhooks | 在 eventos 里显式加上 email.sent 与 email.failed;不订阅就只能靠查询 |
| 3 发送 | POST /openapi/v2/empresas/{empresaId}/emails | 受理即返回 emailId,投递异步 |
| 4 查询 | GET /openapi/v2/empresas/{empresaId}/emails/{emailId} | 状态、等待原因、失败原因、实际发了什么 |
| 辅助 | GET .../emails/templates · GET .../emails/templates/{templateId} | 模板列表与详情,详情带可用变量清单 |
鉴权与其他开放接口相同:请求头 token / timestamp / sign,见认证与签名。邮件只在你调用发送接口时才发,票授权时不会自动发。
模板与发送的关系
- 模板属于集成商(租户级),不绑定主体;任何已注册主体都可以用任何模板。
- 每份模板固定一个发票类型(
tipoDocumento)和一种语言(idioma)。同一类型可以有多份,例如每种语言一份。 - 每个发票类型最多一份默认模板(
padrao=true),不分语言。发送时不传templateId就用它,没有默认返回10021012;要用非默认的模板,发送时传templateId。 - 把某份模板置为默认会自动取消同类型原来的默认;停用(
ativo=false)的模板不能用于发送,也不能当默认。 - 保存是整份覆盖:传
templateId时所有字段都要重传,可选字段不传就回到缺省。 - 发送时模板必须启用且类型与单据一致,否则
10021002。
变量替换
在主题与正文里写 {{名字}},名字以字母开头,可含字母、数字、下划线与点,花括号内可有空格。保存时按发票类型校验白名单,未知名字返回 10021003;渲染时无值的变量成空文本。正文里的取值经 HTML 转义,主题里不转义。
四类单据共有:
| 变量 | 含义 |
|---|---|
numero | 票号 |
serie | 系列号 |
chaveAcesso | 访问密钥,44 位或 50 位 |
dataEmissao | 开票时间,圣保罗时区 |
valorTotal | 票面总额:NF-e 取票总额,NFS-e 取服务金额,CT-e 取运输服务总额,DC-e 取申报总额 |
emitente.nome | 开票主体名称 |
emitente.cnpj | 开票主体 CNPJ |
cliente.nome | 收件方名称:NF-e 买方,NFS-e 与 CT-e 承担方,DC-e 收货人 |
cliente.documento | 收件方证件号,CPF 或 CNPJ |
linkPdf | PDF 凭证下载链接 |
linkXml | 授权 XML 下载链接;XML 尚未归档时为空 |
按类型专属:
| 类型 | 变量 | 含义 |
|---|---|---|
| NFSe | codigoVerificacao | NFS-e 校验码 |
| NFSe | municipio | 开票主体所在市镇名称 |
| NFSe | descricaoServico | 服务描述 |
| NFSe | valorIss | ISS 税额 |
| CTe | remetente.nome | 发货人名称 |
| CTe | destinatario.nome | 收货人名称 |
| CTe | valorFrete | 运费总额 |
| DCe | marketplace.site | 平台站点,取自主体的 DC-e 档案 |
金额与日期按模板 idioma 格式化:pt / es 为 1.234,56 与 dd/MM/yyyy HH:mm;en / zh 为 1,234.56 与 yyyy-MM-dd HH:mm。时刻一律按圣保罗时区展示。查询模板的 variaveisDisponiveis 就是该类型的完整清单。
正文 HTML 与图片
正文只拒绝不清洗:超出白名单的内容在保存时返回 10021004 并点名位置。允许的标签:p、div、span、br、hr、h1 到 h6、strong、b、em、i、u、s、ul、ol、li、blockquote、table、thead、tbody、tr、th、td、a、img;允许的属性:style、href、src、alt、width、height、align、colspan、rowspan。style 里不能有 url(、expression(、position:、behavior:;href 只接受 https:、mailto:、{{linkPdf}}、{{linkXml}};正文上限 64 KB。
图片只能引用 https 地址。 平台没有图片上传接口,logo 请放在你自己的公网地址上。http 图片会被邮件客户端标为混合内容,内联的 data: 图片在部分网页邮箱里不显示,两者都在保存时被拒绝。带 logo 与按钮的完整例子见保存邮件模板。
附件与下载链接
enviarPdf=true附上 PDF 凭证(DANFE / DANFSe / DACTE / DACE),enviarXml=true附上授权 XML 原件;文件名{tipo}-{chave}.pdf/.xml。- 凭证或 XML 尚未就绪时邮件不会缺件发出,而是停在
Aguardando(motivoEspera为pdf或xml),每 30 秒重查,最长 120 分钟,超时转Falha。 - 不附文件也可以在正文里放
{{linkPdf}}/{{linkXml}}下载链接;链接有效期按集成商的文件链接设置,缺省 7 天。
发件人与收件人
- 发件地址是平台地址,不能自定义发件邮箱或域名;显示名为开票主体的公司名,回复地址为主体注册的邮箱,客户回信会到主体那里。
- 收件人
destinatarios最多 5 个;不传时取开票时给的买方邮箱,只有 NF-e 与 NFS-e 有,CT-e 与 DC-e 必须显式传。都没有返回10021013。 - 地址会去重、域名转小写;受理响应与查询里的
destinatarios是归一化后的实际收件人。
状态流转
Aguardando ──► Enfileirado ──► Enviando ──┬──► Enviado(终态)└──► Falha(终态)
| 状态 | 含义 | 是否终态 |
|---|---|---|
Aguardando | 已受理,等票的数据到齐;motivoEspera 为 chaveAcesso / pdf / xml | 否 |
Enfileirado | 待发送,数据已齐 | 否 |
Enviando | 正在渲染与出站 | 否 |
Enviado | 发信服务已接收 | 是 |
Falha | 终态失败,motivoFalha 说明原因 | 是 |
Aguardando 只在缺密钥(圣保罗 NFS-e 的 50 位密钥由全国环境稍后回填)、缺 PDF 或缺 XML 时出现,不消耗重试次数。出站失败按递增间隔重试,最多 5 次,用尽转 Falha。Falha 的其他原因:票已不再是授权态、受理后模板被停用、凭证不可出具。
幂等与 409
id是幂等键,主体内唯一。同id重复请求返回原受理结果,不会再建一封邮件,请求体不同也一样;响应里的status是当前状态。- 同一张票(同主体、同
tipoDocumento、同documentoId)上一封邮件未到终态前,换id再发返回 HTTP 409 与10021015,消息带在途邮件的emailId与状态。等回调或查询到Enviado/Falha后,用新的id重发。
回调事件
在注册 Webhook的 eventos 里显式加上 email.sent 与 email.failed,这两个事件只推给显式订阅的应用。载荷是统一事件体,data 字段名为下划线风格:
{"version": "1.0","event_id": "1950000000000006","event_type": "email.sent","occurred_at": "2026-09-28T12:00:31Z","data": {"email_id": "2103160000000000001","id": "email-pedido-8842-1","tipo_documento": "NFe","documento_id": "pedido-8842","chave": "35241204893402000113650010000117691017244265","destinatarios": ["comprador@exemplo.com"],"status": "Enviado","motivo_falha": null}}
| 事件 | 触发 | data.status |
|---|---|---|
email.sent | 发信服务已接收 | Enviado |
email.failed | 终态失败,data.motivo_falha 说明原因 | Falha |
email.sent 表示发信服务接收了邮件,不代表已进入收件人邮箱。完整字段说明见 Webhooks。
错误模型
对外端点返回无包装的数组:
[{"codigo": "10021013", "mensagem": "Sem destinatário: destinatarios não informado e o documento não tem e-mail do cliente"}]
| 码 | HTTP | 含义 | 处置 |
|---|---|---|---|
10001001 | 400 | 字段校验失败 | 按消息修正 |
10003000 | 404 | 主体不存在 | 核对 empresaId |
10021001 | 404 | 模板不存在或不属于本集成商 | 核对 templateId |
10021002 | 400 | 模板已停用,或类型与单据不符 | 换模板或启用 |
10021003 | 400 | 模板引用了白名单外的变量 | 按消息里的名字修正 |
10021004 | 400 | 模板 HTML 含禁止内容或超过 64 KB | 按消息点名的位置修正 |
10021005 | 400 | 发票类型不支持 | 只用 NFe / NFSe / CTe / DCe |
10021010 | 404 | 发票不存在或不属于本主体 | 核对 documentoId 与 tipoDocumento |
10021011 | 400 | 发票尚未授权 | 等授权后再发 |
10021012 | 400 | 该类型没有默认模板 | 传 templateId 或标一个默认 |
10021013 | 400 | 没有收件人 | 传 destinatarios |
10021015 | 409 | 同票上一封邮件在途 | 等终态后用新 id 重发 |
10021020 | 404 | 邮件不存在或不属于本主体 | 核对 emailId |
鉴权与授权错误(10009xxx)见认证与签名,完整错误码表见错误码。
联调清单
- 保存一份 NFe 模板并标
padrao=true,确认响应里variaveisDisponiveis有 11 项。 - 故意在正文里写
{{codigoVerificacao}},确认收到10021003;把图片改成http地址,确认收到10021004。 - 在 Webhook 的
eventos里加上email.sent与email.failed。 - 对一张已授权的 NF-e 发送(
enviarPdf与enviarXml都为true,不传destinatarios),确认受理为Enfileirado,随后收到email.sent,查询里assunto与enviadoEm有值。 - 在上一封到终态前用另一个
id再发同一张票,确认收到 409 与10021015;用同一个id再发,确认返回原emailId。 - 对一张圣保罗 NFS-e 发送,确认受理为
Aguardando且motivoEspera为chaveAcesso,密钥回填后自动转Enviado。
