Invoice e-mail
Send authorized NF-e / NFS-e / CT-e / DC-e to the customer by e-mail: templates and variables, body HTML and images, attachments and download links, sender rules, states, idempotency and 409, webhook events, error model and an integration checklist.
Capabilities and onboarding flow
| Step | Endpoint | Notes |
|---|---|---|
| 1 Save template | POST /openapi/v2/emails/templates | Integrator-level, shared by every company; several per document type allowed, padrao marks the default |
| 2 Register callback | POST /openapi/v1/webhooks | List email.sent and email.failed in eventos; without a subscription only the query is left |
| 3 Send | POST /openapi/v2/empresas/{empresaId}/emails | Returns the emailId at acceptance; delivery is asynchronous |
| 4 Query | GET /openapi/v2/empresas/{empresaId}/emails/{emailId} | Status, wait reason, failure reason and what was sent |
| Support | GET .../emails/templates · GET .../emails/templates/{templateId} | Template list and detail; the detail carries the list of allowed variables |
Authentication is the same as for the other endpoints: token / timestamp / sign headers, see Authentication. The e-mail only goes out when you call the send endpoint; nothing is sent automatically on authorization.
Templates and sending
- A template belongs to the integrator, not to a company; any registered company uses any template.
- Each template has a fixed document type (
tipoDocumento) and language (idioma). There may be several per type, for example one per language. - Each document type has at most one default template (
padrao=true), regardless of language. A send withouttemplateIduses it; without a default it returns10021012. To use another template, passtemplateIdwhen sending. - Marking a template as default removes the previous default of the same type; a disabled template (
ativo=false) cannot be used for sending nor be the default. - Saving replaces the whole template: with
templateId, resend every field; an omitted optional field goes back to its default. - At send time the template must be enabled and of the document type, otherwise
10021002.
Variables
Write {{nome}} in the subject or the body; the name starts with a letter and accepts letters, digits, underscore and dot, with optional spaces inside the braces. At save time each name is checked against the list of the document type; an unknown name returns 10021003. At render time a variable without a value becomes empty text. In the body the value is HTML-escaped; in the subject it is not.
Common to the four types:
| Variable | Meaning |
|---|---|
numero | Document number |
serie | Series |
chaveAcesso | Access key, 44 or 50 digits |
dataEmissao | Issue time, São Paulo time zone |
valorTotal | Total amount: NF-e total, NFS-e service amount, CT-e total service value, DC-e declared value |
emitente.nome | Issuer company name |
emitente.cnpj | Issuer CNPJ |
cliente.nome | Name of the invoice recipient: NF-e buyer, NFS-e and CT-e service taker, DC-e recipient |
cliente.documento | Recipient document, CPF or CNPJ |
linkPdf | PDF download link |
linkXml | Authorized XML download link; empty if the XML is not archived yet |
Type-specific:
| Type | Variable | Meaning |
|---|---|---|
| NFSe | codigoVerificacao | NFS-e verification code |
| NFSe | municipio | Issuer municipality name |
| NFSe | descricaoServico | Service description |
| NFSe | valorIss | ISS amount |
| CTe | remetente.nome | Cargo sender name |
| CTe | destinatario.nome | Cargo recipient name |
| CTe | valorFrete | Total freight value |
| DCe | marketplace.site | Marketplace site, from the issuer DC-e profile |
Amounts and dates follow the template idioma: pt / es use 1.234,56 and dd/MM/yyyy HH:mm; en / zh use 1,234.56 and yyyy-MM-dd HH:mm. The time is always São Paulo time. The variaveisDisponiveis field of Get template is the full list for the type.
Body HTML and images
The body is refused, not cleaned: anything outside the list returns 10021004 at save time, with its position. Allowed tags: p, div, span, br, hr, h1 to h6, strong, b, em, i, u, s, ul, ol, li, blockquote, table, thead, tbody, tr, th, td, a, img; allowed attributes: style, href, src, alt, width, height, align, colspan, rowspan. style must not contain url(, expression(, position: or behavior:; href only accepts https:, mailto:, {{linkPdf}} or {{linkXml}}; the body is limited to 64 KB.
Images only through an https address. The platform has no image upload; host the logo at a public address of your own. An http image is flagged as mixed content by mail clients and an inline data: image does not show in some webmails; both are refused at save time. A complete example with logo and buttons is in Save e-mail template.
Attachments and download links
enviarPdf=trueattaches the PDF voucher (DANFE / DANFSe / DACTE / DACE) andenviarXml=trueattaches the authorized XML; the files are named{tipo}-{chave}.pdf/.xml.- If the voucher or the XML is not available yet, the e-mail does not go out incomplete: it stays in
Aguardando(motivoEspera=pdforxml), rechecked every 30 seconds for up to 120 minutes; then it becomesFalha. - Even without attachments the body may carry the
{{linkPdf}}/{{linkXml}}links; their validity follows the integrator file-link setting, 7 days by default.
Sender and recipients
- The sender address is the platform address; your own e-mail or domain cannot be used. The display name is the issuer legal name and the reply-to is the company registered e-mail, so customer replies reach the company.
destinatariosaccepts up to 5 addresses; omitted, it uses the customer e-mail given at issuance, which only NF-e and NFS-e carry; CT-e and DC-e requiredestinatarios. With none at all,10021013.- Duplicate addresses are merged and the domain is lowercased;
destinatariosin the acceptance and in the query is the effective, normalized list.
States
Aguardando ──► Enfileirado ──► Enviando ──┬──► Enviado (terminal)└──► Falha (terminal)
| Status | Meaning | Terminal |
|---|---|---|
Aguardando | Accepted, waiting for document data; motivoEspera = chaveAcesso / pdf / xml | No |
Enfileirado | Queued, data complete | No |
Enviando | Rendering and sending | No |
Enviado | Accepted by the mail service | Yes |
Falha | Final failure, motivoFalha explains | Yes |
Aguardando only exists while the key (the 50-digit key of a São Paulo NFS-e comes back later through the national environment), the PDF or the XML is missing, and it does not consume attempts. A failure on the way out is retried with growing backoff, up to 5 attempts; exhausted, it becomes Falha. Other causes of Falha: document no longer authorized, template disabled after acceptance, voucher that cannot be issued.
Idempotency and 409
idis the idempotency key, unique within the company. Repeating the sameidreturns the original acceptance without creating another e-mail, even with a different body; thestatusin the response is the current state.- While the previous e-mail of the same document (same company,
tipoDocumentoanddocumentoId) has not reached a terminal state, a send with anotheridreturns HTTP 409 and10021015, with theemailIdand status of the in-flight e-mail in the message. Wait forEnviado/Falhathrough the callback or the query, then resend with a newid.
Webhook events
List email.sent and email.failed in eventos when registering the webhook; both are only delivered to applications that subscribe to them explicitly. The payload is the event envelope, with data fields in snake_case:
{"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}}
| Event | When | data.status |
|---|---|---|
email.sent | The mail service accepted the message | Enviado |
email.failed | Final failure, data.motivo_falha explains | Falha |
email.sent means the mail service accepted the message, not that it reached the recipient inbox. The full field description is in Webhooks.
Error model
The endpoints return a bare array:
[{"codigo": "10021013", "mensagem": "Sem destinatário: destinatarios não informado e o documento não tem e-mail do cliente"}]
| Code | HTTP | Meaning | What to do |
|---|---|---|---|
10001001 | 400 | Field validation failed | Fix as the message says |
10003000 | 404 | Company not found | Check empresaId |
10021001 | 404 | Template not found or owned by another integrator | Check templateId |
10021002 | 400 | Template disabled or of a different type than the document | Switch template or re-enable it |
10021003 | 400 | Template uses variables outside the allowed list | Fix the names listed in the message |
10021004 | 400 | Template HTML has forbidden content or exceeds 64 KB | Fix the positions listed in the message |
10021005 | 400 | Unsupported document type | Use only NFe / NFSe / CTe / DCe |
10021010 | 404 | Document not found or owned by another company | Check documentoId and tipoDocumento |
10021011 | 400 | Document not authorized yet | Send after authorization |
10021012 | 400 | Type without a default template | Pass templateId or mark a default |
10021013 | 400 | No recipient | Pass destinatarios |
10021015 | 409 | Previous e-mail of the same document in flight | Wait for the terminal state and resend with a new id |
10021020 | 404 | E-mail not found or owned by another company | Check emailId |
Authentication and authorization errors (10009xxx) are in Authentication; the full table is in Error codes.
Integration checklist
- Save an NFe template with
padrao=trueand check thatvariaveisDisponiveisin the response has 11 items. - Deliberately write
{{codigoVerificacao}}in the body and check10021003; switch the image to anhttpaddress and check10021004. - List
email.sentandemail.failedin the webhookeventos. - Send an authorized NF-e (
enviarPdfandenviarXmlset totrue, nodestinatarios); check theEnfileiradoacceptance, thenemail.sent, andassuntoandenviadoEmfilled in the query. - Before the terminal state, send the same document with another
idand check 409 with10021015; repeat with the sameidand check the sameemailId. - Send a São Paulo NFS-e and check the
Aguardandoacceptance withmotivoEspera=chaveAcesso, moving toEnviadoon its own once the key arrives.
