CFDI cancellation
To cancel a CFDI (Mexico's electronic invoice), the issuer sends a request signed with its CSD (Certificado de Sello Digital, the digital seal certificate used to sign CFDIs) to the SAT (Mexico's tax authority, Servicio de Administración Tributaria). Fiscalapi submits it for you. In the current scheme, each request carries a cancellation reason (one of the SAT keys 01 to 04). Depending on the invoice, the recipient may have to accept it, so the cancellation isn't always immediate. This tutorial explains the SAT rules and how Fiscalapi notifies you of the result. The requests and responses are documented in Cancel an invoice.
Cancellation reasons
Send the reason in cancellationReasonCode. The details for each reason are in the cancellation reasons catalog.
| Reason | When to use it | What it requires |
|---|---|---|
"01" | The invoice has errors, and you replace it with a new CFDI. | Issue the replacement CFDI first, related to the original with relationship type "04", and send its UUID (folio fiscal, the unique ID of each stamped CFDI) in replacementUuid. |
"02" | The invoice has errors, and you don't replace it, for example because the recipient's RFC (Mexico's taxpayer ID, Registro Federal de Contribuyentes) is wrong. | Only the reason. If needed, issue the correct invoice later. |
"03" | The transaction did not take place. | Only the reason. |
"04" | A sale included in a global invoice (factura global, one CFDI that groups a period's sales to the general public) is invoiced in the customer's own name. | Cancel the global invoice, reissue it without that sale, and issue the invoice in the customer's name. |
An invoice with at least one related document that is Vigente (active) is No cancelable (not cancelable). The exception is reason "01": the original invoice becomes No cancelable when it's related to its replacement, and that link breaks when you request its cancellation.
Recipient acceptance
Depending on the invoice, the SAT cancels it without the recipient's involvement, or asks the recipient to accept the cancellation. Before you cancel, use Check an invoice's status. cancelableStatus shows whether the invoice is cancelable sin aceptación (cancelable without acceptance), cancelable con aceptación (cancelable with acceptance), or No cancelable (not cancelable).
The SAT doesn't require the recipient's acceptance in these cases under Rule 2.7.1.35 of the Resolución Miscelánea Fiscal (RMF, the SAT's annual tax rules):
| Case | Invoices |
|---|---|
| Amount | Invoices that cover a total of up to MXN 1,000.00. |
| Time limit | Invoices canceled by the next business day after they're issued. |
| CFDI type | Payroll CFDIs (type N, the CFDIs issued for each salary payment), credit notes (Egreso CFDIs, type E), transfers (type T, CFDIs that move goods without a sale), and withholding CFDIs (CFDI de Retenciones e Información de Pagos, the CFDIs that report taxes withheld from a payment). |
| Recipient | Sales to the general public (customers who did not ask for their own CFDI), invoices to foreign residents, and income CFDIs (type I, the regular sales invoices) for taxpayers in the RIF (Régimen de Incorporación Fiscal, a small-taxpayer regime closed to new taxpayers since 2022). |
| Issuer or origin | Invoices issued in Mis cuentas (the SAT's free invoicing app for small taxpayers), by members of the financial system, by the Federation for derechos, productos y aprovechamientos (federal fees, product charges, and other revenues), and CFDIs issued through the buyer (adquirente: the buyer who issues the CFDI on the seller's behalf) and by the primary sector (agriculture, livestock, forestry, and fishing); see Rules 2.7.3.10 and 2.7.4.1 of the RMF. |
The SAT excludes from these cases the CFDIs with a payment complement (payment complement: the complement that records each payment received against a PPD invoice; the type P CFDI that carries it is also called REP), regardless of their amount. It also excludes CFDIs for hydrocarbons and petroleum products: those that carry their complement, and the CFDIs for Carta Porte (the CFDI complement that documents the transport of goods in Mexico) with SAT product or service keys 15101505, 15101514, or 15101515.
In other cases, the SAT notifies the recipient through the SAT tax mailbox (buzón tributario: the SAT's official channel to notify taxpayers), and the recipient has three business days to accept or reject the request. If the recipient doesn't respond to the first request, the SAT treats it as accepted and cancels the invoice. If the recipient rejected the first request and doesn't respond to a second or later request, the cancellation doesn't proceed: the invoice stays Vigente (active) until the recipient accepts.
Request states at the SAT
The SAT query returns the Estado de la factura (invoice state) and the Estatus de cancelación (cancellation status) of the request. In the API, they arrive in status and cancellationStatus from Check an invoice's status.
| State | Cancellation status | Meaning |
|---|---|---|
| Vigente (active) | vacío (empty) | There is no cancellation request. |
| Vigente (active) | En proceso (in progress) | The recipient has the request and hasn't responded yet. |
| Vigente (active) | Solicitud rechazada (request rejected) | The recipient rejected the cancellation. |
| Vigente (active) | Plazo vencido (deadline expired) | The recipient didn't respond to a second or later request. The cancellation doesn't proceed. |
| Cancelado (canceled) | Cancelado sin aceptación (canceled without acceptance) | The SAT canceled the invoice without requiring the recipient's acceptance. |
| Cancelado (canceled) | Cancelado con aceptación (canceled with acceptance) | The recipient accepted the cancellation. |
| Cancelado (canceled) | Plazo vencido (deadline expired) | The recipient didn't respond to the first request, and the SAT treated it as accepted. |
With the state No Encontrado (not found), the SAT didn't find the invoice with the query data. A newly stamped invoice (stamping: the certification by a SAT-authorized provider that makes a CFDI valid) can take a while to appear.
How Fiscalapi handles it
Request response
Send the request with Cancel an invoice. Before Fiscalapi sends it to the SAT, it validates the invoice, the reason, the replacement CFDI, and the issuer's certificates. If something fails, it returns an error, for example a 400 validation error, and doesn't send the cancellation.
| SAT response | Fiscalapi response |
|---|---|
| Received the request (201 or 202). | 200. The Vigente (active) invoice moves to Cancelando (canceling). |
| Rejected the request. | 422, with details in the form SAT <code>: <description>. The invoice's state doesn't change. |
A 200 means the SAT received the request, not that the invoice is already canceled. For a 422, see the SAT rejection codes and what to do when a request is rejected.
Invoice states
| Code | State | Meaning |
|---|---|---|
"V" | Vigente (active) | The invoice is valid and accepts a cancellation request. |
"C" | Cancelando (canceling) | The SAT received the request, and Fiscalapi is waiting for its result. It accepts another request. |
"X" | Cancelado (canceled) | The SAT confirmed the cancellation. The invoice doesn't return to another state. |
After an accepted request, Fiscalapi checks with the SAT. It moves the invoice to Cancelado (canceled) when the SAT reports it as canceled, or returns it to Vigente (active) when the cancellation doesn't proceed. While the SAT has it En proceso (in progress), Fiscalapi checks it after 1 minute and then with waits that double (2, 4, 8 minutes, and so on) up to one check every 12 hours, for about 10 days. After that, it checks once a day until 30 days after the last accepted request. If the SAT doesn't resolve it by then, the invoice stays Cancelando (canceling) and accepts another request.
Tracking the result
Subscribe to a webhook to learn about each state change without querying the API:
| Event | When it happens |
|---|---|
invoice.cancellation_requested | The SAT received the request: the invoice moves from Vigente (active) to Cancelando (canceling). |
invoice.canceled | The SAT confirmed the cancellation: the invoice moves from Cancelando (canceling) to Cancelado (canceled). |
invoice.cancellation_rejected | The cancellation doesn't proceed: the invoice goes back from Cancelando (canceling) to Vigente (active). The reason arrives in data.reason. |
To read the state at the SAT at any time, use Check an invoice's status and read its values with the state table.
Test environment
In the test environment, the SAT is simulated, and invoices aren't valid for tax purposes. An accepted request is confirmed as Cancelado (canceled) on the first check, about a minute later.
What to do when a request is rejected
The rejection of the request arrives in the 422 response with the SAT code. A later rejection arrives in invoice.cancellation_rejected, with the reason in data.reason.
| Rejection | What it means | What to do |
|---|---|---|
| SAT 205 | The SAT doesn't reflect the invoice yet because it was stamped recently. | Request the cancellation again later. It can take up to 48 hours. |
| SAT 203 | The UUID doesn't match the issuer of the request. | Check that the RFC and the CSD you use to cancel belong to the invoice's issuer. |
| SAT 312 | You sent a replacement UUID with a reason that doesn't use it. | Remove replacementUuid, or use reason "01". |
"rejectedByRecipient" | The recipient rejected the cancellation. | Agree on the cancellation with the recipient, and request it again. |
"expired" | The recipient didn't respond to a second or later request. | Agree on the cancellation with the recipient. The cancellation proceeds only when the recipient accepts it. |
"notCancelable" | The SAT reports the invoice as No cancelable (not cancelable), for example because it has a related document that is Vigente (active). | Cancel the related documents that are Vigente (active) first. If the invoice has a replacement CFDI, cancel it with reason "01". |
"notRequested" | The SAT doesn't have the cancellation request. | Request the cancellation again. |
Example
This request cancels an invoice by reference with reason "02", which has no replacementUuid.
Request
curl --location --request DELETE 'https://test.fiscalapi.com/api/v4/invoices' \
--header 'X-TENANT-KEY: <tenant>' \
--header 'X-TIME-ZONE: America/Mexico_City' \
--header 'Content-Type: application/json' \
--header 'X-API-KEY: <apiKey>' \
--data '{
"id": "21c39425-9d0a-449b-a7ba-49335b85b062",
"cancellationReasonCode": "02"
}'
If the SAT rejects the request, Fiscalapi responds with 422 and the SAT code for each UUID, along with the cancellation acknowledgment. In this example, the UUID doesn't match the issuer, and the Base64 acknowledgment is abbreviated.
Response
{
"data": {
"base64CancellationAcknowledgement": "PD94bWwgdmVyc2lvbj0iMS4wIj8+...",
"invoiceUuids": {
"<UUID>": "203"
}
},
"succeeded": false,
"message": "Sorry, it's not me, it's you.",
"details": "SAT 203: el folio fiscal no corresponde al emisor de la solicitud de cancelación.",
"httpStatusCode": 422
}