Events: Cancellation and Correction Letter
Tutorial for cancelling an authorized NF-e or issuing a Correction Letter (CC-e) through the NF-e SP connector.
Overview
After an NF-e is authorized, you may need to either annul it (cancellation) or fix a non-monetary field (Carta de Correção / CC-e). Both go through SEFAZ as events, identified by a tp_evento code.
The connector provides two high-level actions that take the minimum required input and assemble the full event payload internally:
| Action | Event code | Use for |
|---|---|---|
nfe_cancelar | 110111 | Cancel an authorized NF-e |
nfe_carta_correcao | 110110 | Correct typos and informational fields (CC-e) |
For any other event (EPEC 110140, manifesto do destinatário 210xxx, etc.) use recepcao_evento with the full payload — see Advanced events below.
Important: Both actions require the authorization protocol (
n_prot) of the NF-e you're acting on. Get it vianfe_consulta_protocoloif you don't have it stored — see Getting the protocol.
Cancelling an NF-e
Cancellation annuls an authorized NF-e. Only allowed within SEFAZ's window after authorization (24h regular, 168h in contingency).
from abstra.connectors import run_connection_action
result = run_connection_action(
"nfe-sp",
"nfe_cancelar",
{
"ch_nfe": "35260112345678000100550010000000011123456780", # Access key (44 digits)
"n_prot": "123456789012345", # Authorization protocol
"x_just": "Pedido cancelado pelo cliente em 02/06/2026", # Justification (min 15 chars)
},
)
That's it — ch_nfe, n_prot and x_just are the only fields you need to provide.
Required fields
| Field | Type | Rule |
|---|---|---|
ch_nfe | string | Exactly 44 digits |
n_prot | string | Authorization protocol of the NF-e being cancelled (from nfe_consulta_protocolo or stored when you authorized it) |
x_just | string | Justification text. Between 15 and 255 characters |
Optional fields
| Field | Type | Default |
|---|---|---|
cnpj | string (14 digits) | Extracted from positions 6–19 of ch_nfe |
n_seq_evento | string (number) | "1". Increase only when retrying after a rejection on the same NF-e |
dh_evento | string (ISO 8601) | Current time in São Paulo timezone (-03:00) |
Reusable helper
from abstra.connectors import run_connection_action
def cancelar_nfe(chave: str, protocolo: str, justificativa: str) -> dict:
"""Cancela uma NF-e autorizada. Retorna a resposta da SEFAZ."""
return run_connection_action(
"nfe-sp",
"nfe_cancelar",
{
"ch_nfe": chave,
"n_prot": protocolo,
"x_just": justificativa,
},
)
result = cancelar_nfe(
chave="35260112345678000100550010000000011123456780",
protocolo="123456789012345",
justificativa="Pedido cancelado - estoque indisponível em 02/06/2026",
)
Reading the response
A successful response carries c_stat 135 (event registered and linked) or 136 (registered, will link asynchronously). Both mean the cancellation went through.
ret = result["soap_envelope"]["soap_body"]["nfe_result_msg"]["ret_env_evento"]
# Batch-level status (acceptance of the lote)
print(ret["c_stat"], "-", ret["x_motivo"]) # e.g. "128 - Lote de Evento Processado"
# Event-level status (the actual cancellation outcome)
inf_evento = ret["ret_evento"][0]["inf_evento"] if isinstance(ret.get("ret_evento"), list) else ret["ret_evento"]["inf_evento"]
print(inf_evento["c_stat"], "-", inf_evento["x_motivo"]) # e.g. "135 - Evento registrado e vinculado a NF-e"
print("Cancellation protocol:", inf_evento.get("n_prot"))
c_stat | Meaning |
|---|---|
135 | Event registered and linked to the NF-e (success) |
136 | Event registered, not yet linked (success — will link asynchronously) |
573 | Cancellation already registered (idempotent — treat as success) |
Issuing a Correction Letter (CC-e)
A CC-e (Carta de Correção Eletrônica) fixes typos and informational fields on an authorized NF-e. Up to 20 CC-e are allowed per NF-e.
result = run_connection_action(
"nfe-sp",
"nfe_carta_correcao",
{
"ch_nfe": "35260112345678000100550010000000011123456780",
"n_prot": "123456789012345",
"x_correcao": "Onde se le 'Rua das Flores 100', leia-se 'Rua das Flores 200'.",
},
)
Required fields
| Field | Type | Rule |
|---|---|---|
ch_nfe | string | Exactly 44 digits |
n_prot | string | Authorization protocol of the NF-e |
x_correcao | string | Correction text. Between 15 and 1000 characters |
Optional fields
| Field | Type | Default |
|---|---|---|
cnpj | string (14 digits) | Extracted from ch_nfe |
n_seq_evento | string (number) | "1". Must increase by 1 for each new CC-e on the same NF-e (max 20) |
dh_evento | string (ISO 8601) | Now in São Paulo timezone |
x_cond_uso | string | Standard legal text per §1º-A, art. 7º, Convênio S/N. Only override if you have a specific need |
What a CC-e CANNOT correct
A CC-e is not allowed for:
| Cannot correct via CC-e | Use instead |
|---|---|
| Tax variables (base de cálculo, alíquota, valor do imposto, preço, quantidade) | Cancel and re-issue |
| Cadastral data of emitter or recipient (CNPJ, name, address of the other party) | Cancel and re-issue |
| Emission date or shipping date | Cancel and re-issue |
If your correction falls in this table, use nfe_cancelar and emit a new invoice with the right data.
Multiple CC-e on the same NF-e
For the second CC-e on the same invoice, increment n_seq_evento:
# First CC-e
run_connection_action("nfe-sp", "nfe_carta_correcao", {
"ch_nfe": chave,
"n_prot": protocolo,
"x_correcao": "Primeira correção: ...",
# n_seq_evento defaults to "1"
})
# Second CC-e on the SAME NF-e
run_connection_action("nfe-sp", "nfe_carta_correcao", {
"ch_nfe": chave,
"n_prot": protocolo,
"x_correcao": "Segunda correção: ...",
"n_seq_evento": "2",
})
Getting the protocol
If you don't already have n_prot stored from when the NF-e was authorized, query SEFAZ:
consulta = run_connection_action(
"nfe-sp",
"nfe_consulta_protocolo",
{
"tp_amb": "1", # "1" producao, "2" homologacao
"x_serv": "CONSULTAR",
"ch_nfe": "35260112345678000100550010000000011123456780",
"versao": "4.00",
},
)
ret_cons = consulta["soap_envelope"]["soap_body"]["nfe_result_msg"]["ret_cons_sit_nfe"]
n_prot = ret_cons["prot_nfe"]["inf_prot"]["n_prot"]
Advanced events
For events other than cancellation and CC-e (EPEC 110140, manifesto do destinatário 210200/210210/210220/210240, etc.) use recepcao_evento directly. The full structure is:
run_connection_action("nfe-sp", "recepcao_evento", {
"evento": [{
"inf_evento": {
"cnpj": "12345678000100",
"ch_nfe": "35260112345678000100550010000000011123456780",
"tp_evento": "110111", # event code
"det_evento": {
"n_prot": "123456789012345",
"x_just": "Justificativa...",
# event-specific fields go here
},
}
}]
})
The connector auto-fills deterministic fields when omitted: id_lote, versao (envelope and evento), c_orgao, tp_amb, dh_evento, n_seq_evento, ver_evento, and inf_evento.id. For events 110110 and 110111 it also fills det_evento.versao and det_evento.desc_evento. For cancellation and CC-e, prefer nfe_cancelar / nfe_carta_correcao — they require 3 fields instead of 15.
Auto-computed fields
The connector handles these so you don't have to. Do not pass them — they will be overwritten or generated:
| Field | How the connector computes it |
|---|---|
inf_evento.id | ID + tp_evento (6 digits) + ch_nfe (44 digits) + n_seq_evento zero-padded to 2 digits → 52 digits total. c_orgao is NOT part of the Id — a common mistake that triggers rejection 225 |
inf_evento.c_orgao | First 2 digits of ch_nfe (UF code, e.g. "35" for SP) |
inf_evento.tp_amb | "1" if the connection's environment is producao, "2" if homologacao |
inf_evento.dh_evento | Current time in São Paulo timezone (-03:00), ISO 8601 |
inf_evento.ver_evento | "1.00" |
det_evento.versao | "1.00" (XML attribute on <detEvento>) |
det_evento.desc_evento | "Cancelamento" for 110111, "Carta de Correcao" for 110110 |
evento.versao | "1.00" |
id_lote | Unix timestamp in seconds |
For LLM tooling: if you're generating a payload for
nfe_cancelarornfe_carta_correcao, only emit the 3 required fields plus any optional ones the user mentioned. Do not invent values for the auto-computed fields above.
Validation before SEFAZ
The connector throws a BadRequestError (HTTP 400) before sending to SEFAZ when:
ch_nfeis missing or not exactly 44 digitsn_protis missing- (
nfe_carta_correcao)x_correcaois missing
These are validated locally to give a clearer error than SEFAZ's generic 225 - Falha no Schema XML.
Idempotency
Both nfe_cancelar and nfe_carta_correcao are safe to retry. If SEFAZ has already registered the event, it returns c_stat=573 ("Evento registrado anteriormente") — treat this as success.
Common errors
See Troubleshooting for cancellation/CC-e specific errors. The most common are:
c_stat | Meaning | Fix |
|---|---|---|
218 | Cancellation window expired | Cannot cancel — emit a return invoice (fin_nfe: "4") |
225 | Schema validation failure | Check that you used nfe_cancelar / nfe_carta_correcao (not raw recepcao_evento with a malformed id) |
573 | Event already registered | Idempotent — treat as success |
597 | Wrong protocol for this access key | Re-query n_prot via nfe_consulta_protocolo |