Events¶
An event is a document in its own right, with structure and signature
independent from the invoice it refers to. Once registered, it is stored
alongside its response in a procEventoNFe — the analogue of nfeProc for
events.
The evento package
covers:
| Event | Code | Who registers it |
|---|---|---|
| Letter of correction | 110110 | Issuer |
| Cancellation | 110111 | Issuer |
| Cancellation by replacement | 110112 | NFC-e issuer |
| Operation confirmed | 210200 | Recipient |
| Operation acknowledged | 210210 | Recipient |
| Operation not recognised | 210220 | Recipient |
| Operation not carried out | 210240 | Recipient |
Voiding of number ranges also lives in this package. It is not an event in the layout — it is a separate document, sent to a different service — but it shares the signing pattern and the path to SEFAZ.
The flow, in three steps¶
Every event follows the same path:
flowchart LR
A[Build the event] --> B[Sign infEvento]
B --> C[Send as a batch]
C --> D[Store the procEventoNFe]
e, err := evento.NovoCancelamento(evento.DadosCancelamento{ /* … */ })
assinado, err := e.AssinarCom(cert)
ret, err := cliente.EnviarEvento(ctx, assinado)
proc, err := evento.MontarProcEvento(assinado, ret)
A refused batch and a refused event are different things
EnviarEvento returns an error when SEFAZ refuses the batch. When the
batch is accepted but the event is refused — a cancellation past its
deadline, say — there is no error: the reason is in the response's status
code. Always check:
Cancellation¶
Cancelling requires the authorisation protocol number — the same one SEFAZ
returned when the invoice was authorised. If you did not keep it, recover it
with cliente.ConsultarNFe(ctx, chave).
e, err := evento.NovoCancelamento(evento.DadosCancelamento{
Chave: chave,
CNPJ: "12345678000195",
UF: uf.RS,
Ambiente: nfe.Homologacao,
Protocolo: prot.InfProt.NProt,
Justificativa: "Cancelamento por erro de digitacao no pedido do cliente",
})
The justification must be between 15 and 255 characters — the library refuses it before spending a round trip to SEFAZ.
The deadline
As a rule 24 hours from authorisation, with variations by state. Past that, SEFAZ answers with code 501 and the invoice can only be undone through a voluntary disclosure, outside the system.
Denied invoices (code 110) cannot be cancelled: they are born without effect.
Cancellation by replacement¶
Some states allow cancelling an NFC-e by pointing at the one that replaced it:
e, err := evento.NovoCancelamentoPorSubstituicao(evento.DadosCancelamentoPorSubstituicao{
Chave: chaveOriginal,
CNPJ: cnpj,
UF: uf.RS,
Ambiente: nfe.Producao,
Protocolo: protocoloOriginal,
Justificativa: "Cancelamento por substituicao apos correcao do pedido",
ChaveSubstituta: chaveNova,
})
Letter of correction¶
Corrects information that does not change the tax value, the identification
of the parties, or the issue and departure dates. That restriction is the legal
clause that goes in the xCondUso field, filled in automatically from
evento.TextoCondicaoDeUso.
e, err := evento.NovaCartaCorrecao(evento.DadosCartaCorrecao{
Chave: chave,
CNPJ: cnpj,
UF: uf.RS,
Ambiente: nfe.Producao,
Sequencia: 1,
Correcao: "Fica corrigido o endereco de entrega para Rua Nova, 100, Centro",
})
Each letter replaces the previous one
The last letter registered is the one that counts — it is not a list of corrections that accumulates. If you issued letter 1 correcting the address and then need to correct the carrier, letter 2 must repeat the address correction and add the new one. Otherwise the earlier correction ceases to apply.
The text runs from 15 to 1000 characters, and up to 20 letters are allowed per invoice, with an always-increasing sequence number.
Recipient acknowledgement¶
The four acknowledgements are registered by whoever receives the invoice, not by whoever issues it. They go to the National Environment rather than the state SEFAZ — the client handles that detour on its own.
e, err := evento.NovaManifestacao(evento.DadosManifestacao{
Chave: chave,
CNPJ: cnpjDoDestinatario,
Ambiente: nfe.Producao,
Tipo: evento.TipoConfirmacaoOperacao,
})
| Type | When to use it |
|---|---|
TipoCienciaOperacao |
You know the invoice exists, but are not committing yet |
TipoConfirmacaoOperacao |
The operation happened as described |
TipoDesconhecimentoOperacao |
You do not recognise the operation |
TipoOperacaoNaoRealizada |
The operation was yours, but it did not go through |
Only "operation not carried out" accepts — and requires — a justification:
e, err := evento.NovaManifestacao(evento.DadosManifestacao{
Chave: chave,
CNPJ: cnpj,
Ambiente: nfe.Producao,
Tipo: evento.TipoOperacaoNaoRealizada,
Justificativa: "Mercadoria nao foi entregue pelo transportador no prazo",
})
Note that DadosManifestacao has no UF field: the destination is always the
National Environment.
Voiding number ranges¶
Voiding declares to the tax authority that a range of numbers was not and will not be used — usually because a failure in the issuing system consumed numbers without producing documents. It does not undo invoices; cancellation exists for that.
i, err := evento.NovaInutilizacao(evento.DadosInutilizacao{
UF: uf.RS,
Ambiente: nfe.Producao,
CNPJ: "12345678000195",
Ano: 26, // two digits
Modelo: nfe.ModeloNFe,
Serie: 1,
NumeroInicial: 145,
NumeroFinal: 150,
Justificativa: "Falha no sistema emissor durante a geracao dos numeros",
})
assinada, err := i.AssinarCom(cert) // signs the infInut group
ret, err := cliente.Inutilizar(ctx, assinada)
if ret.Homologada() {
proc, _ := evento.MontarProcInut(assinada, ret)
}
The range must be contiguous and belong entirely to the same series and year. If
any number in the range has an authorised document, SEFAZ refuses the whole
request — there is no partial voiding. That is why, unlike events, Inutilizar
returns an error when the status is not 102.
Sending as a batch¶
A batch holds up to twenty events, and all of them must share the same destination:
lote := [][]byte{carta1, carta2, carta3}
resposta, err := cliente.EnviarLoteDeEventos(ctx, "42", lote...)
for _, ret := range resposta.RetEvento {
fmt.Println(ret.Resumo())
}
Mixing an acknowledgement with a cancellation would make SEFAZ refuse the entire batch, because the two go to different servers. The library detects that before transmitting and explains the conflict.
Storing the result¶
The procEventoNFe is the event's receipt. It joins the signed event with the
response, preserving byte for byte the data that was signed:
proc, err := evento.MontarProcEvento(assinado, ret)
os.WriteFile(chave+"-"+string(e.Tipo())+"-procEvento.xml", nfe.XMLDeclarado(proc), 0o644)
For a cancellation, that file must be kept for the same period as the invoice
and handed to whoever received the original nfeProc.
To read it back:
Listing the events on an invoice¶
A lookup by key returns every event already registered:
consulta, err := cliente.ConsultarNFe(ctx, chave)
for _, p := range consulta.ProcEventoNFe {
i := p.RetEvento.InfEvento
fmt.Printf("%s %s em %s\n", string(i.TpEvento), i.XEvento, i.DhRegEvento)
}
Return codes¶
| Code | Meaning |
|---|---|
| 128 | Event batch processed — read each event's response |
| 135 | Event registered and linked to the invoice |
| 136 | Event registered, but not yet linked — the invoice was not in the database |
| 101 | Cancellation approved |
| 102 | Voiding approved |
| 501 | Cancellation past the deadline |
| 573 | Duplicate event — that sequence number was already used |
Registrado() covers 101, 135 and 136; Vinculado() covers 101 and 135. Code
136 is a success: the event holds, and the link happens once the invoice
arrives.
Runnable example¶
exemplos/eventos
registers cancellations, letters of correction, acknowledgements and voidings
from the command line:
export GONFE_CERT=./certificado.pfx
export GONFE_SENHA=...
go run ./exemplos/eventos cancelar -chave 4326... -protocolo 1432... -just "Erro de digitacao no pedido"
go run ./exemplos/eventos corrigir -chave 4326... -texto "Fica corrigido o endereco de entrega"
go run ./exemplos/eventos manifestar -chave 4326... -tipo ciencia
go run ./exemplos/eventos inutilizar -serie 900 -de 10 -ate 12 -just "Falha no sistema emissor"