Motivos 01 a 04
02 si no mandas motivo; 01 con el UUID que sustituye; 03 si la operación no se llevó a cabo; 04 para una operación nominativa relacionada en una factura global.
Envías el UUID y el motivo, y la API tramita la cancelación ante el SAT con tu CSD. La respuesta trae el resultado y el acuse del SAT en Base64. Cancelar no usa folio, y si timbras XML ya sellado, cancelas enviando tu CSD en la petición.
El trámite completo de cancelación de un CFDI 4.0, desde tu sistema.
02 si no mandas motivo; 01 con el UUID que sustituye; 03 si la operación no se llevó a cabo; 04 para una operación nominativa relacionada en una factura global.
Si la solicitud se acepta, recibes el acuse en Base64 y el CFDI queda como cancelado en tu cuenta, con su acuse guardado.
/invoice/cancel usa el CSD que tienes cargado. /invoice/cancelwithcsd recibe tu .cer, tu .key y la contraseña en la petición.
Ninguna de las dos operaciones descuenta folios. /invoice/cancel sí pide que tu cuenta tenga saldo mayor a cero.
GET /validator/cfdi consulta en el SAT el estado del CFDI, si es cancelable y el estatus de su cancelación.
Las constancias de retenciones se cancelan con /invoice/cancelretention, con los mismos headers y los mismos motivos.
Cancelación con motivo 02. Los parámetros van en headers HTTP, no en el body; la llave va en X-Api-Key, como en el resto de la API.
# FD_API=https://sandbox-app.facturadigital.com.mx/api/v5
curl -X POST "$FD_API/invoice/cancel" \
-H "X-Api-Key: $FD_API_KEY" \
-H "UUID: 6FA90F39-2D7A-4F1C-9B3E-5C1D8A0E7B42" \
-H "MOTIVO: 02"{
"message": "Solicitud de cancelación exitosa * Se considera una solicitud de cancelación exitosa, sin embargo esto no asegura su cancelación",
"code": 200,
"acuse": "PD94bWwgdmVyc2lvbj0iMS4wIj8+PEFjdXNlLi4u",
"date": "2026-09-28 10:20:00",
"uuid": "6FA90F39-2D7A-4F1C-9B3E-5C1D8A0E7B42",
"uuid_estatus": 201,
"env_mode": "Sandbox",
"estatus_uuid": 201,
"msg_error": null,
"msg_error_detailt": null
}# Motivo 01 con tu CSD en la petición (el CFDI nuevo ya está timbrado)
curl -X POST "$FD_API/invoice/cancelwithcsd" \
-H "X-Api-Key: $FD_API_KEY" \
--data-urlencode "uuid=6FA90F39-2D7A-4F1C-9B3E-5C1D8A0E7B42" \
--data-urlencode "motivo=01" \
--data-urlencode "folioSustitucion=0B7C2E51-8A4D-4E0F-A1C3-7D9E6F2B5A10" \
--data-urlencode "b64Cer@csd-cer.b64" \
--data-urlencode "b64Key@csd-key.b64" \
--data-urlencode "passwordCSD=$CSD_PASSWORD"Del motivo a la confirmación en el SAT.
02 si el CFDI tiene errores y no lo vas a sustituir. 01 si lo reemplazas: primero timbras el nuevo con CfdiRelacionadosTipoRelacion "04" y el UUID original en CfdiRelacionados.
POST /invoice/cancel con los headers UUID y MOTIVO, más FOLIOSUSTITUCION si el motivo es 01. Si no tienes CSD en tu cuenta, usa /invoice/cancelwithcsd.
La respuesta es HTTP 200 aunque el SAT no acepte la solicitud. code 200 es solicitud aceptada; con cualquier otro, revisa message, msg_error y msg_error_detailt.
Una solicitud aceptada no asegura la cancelación. Consulta el estatus con GET /validator/cfdi y los parámetros uuid, rfcemisor, rfcreceptor y total.
Los errores de tu petición (llave, UUID, saldo, parámetros) responden con HTTP 400. El resultado de la cancelación llega con HTTP 200 y su code en el cuerpo.
Timbra CFDI de ingreso y traslado con el complemento Carta Porte y las reglas del SAT revisadas antes de timbrar.
Timbra el REP de tus facturas PPD con el mismo endpoint y la misma llave.
Prueba cancelaciones gratis con la misma API v5 que producción.
Que tu sistema se entere de lo que pasa en tu cuenta sin consultar la API.
Librerías para llamar la API v5 desde tu lenguaje, con los mismos endpoints.
Autenticación, endpoints y las tres formas de timbrar: JSON, XML en Base64 o archivo XML.
¿Algo que no está aquí? Escríbenos o revisa la referencia completa.
No. Ni /invoice/cancel ni /invoice/cancelwithcsd descuentan folios. /invoice/cancel sí pide que tu cuenta tenga saldo mayor a cero (si no, responde code 601); /invoice/cancelwithcsd no revisa el saldo.
01: comprobante emitido con errores con relación; requiere el UUID que lo sustituye. 02: comprobante emitido con errores sin relación; es el que se usa si no mandas motivo. 03: no se llevó a cabo la operación. 04: operación nominativa relacionada en una factura global.
Primero timbra la nueva con CfdiRelacionadosTipoRelacion "04" y el UUID de la original en CfdiRelacionados. Después cancela la original con MOTIVO 01 y el UUID de la nueva en FOLIOSUSTITUCION.
Porque la respuesta trae el resultado de la solicitud, sea cual sea. Lee code: 200 es solicitud aceptada; cualquier otro valor trae el motivo en message, msg_error y msg_error_detailt. Los errores de tu petición, como una llave inválida o un UUID faltante, sí responden HTTP 400.
Quiere decir que el SAT aceptó la solicitud (uuid_estatus 201), y eso no asegura la cancelación. Si la cancelación requiere la aceptación de tu cliente, queda en proceso hasta que responda. Consulta el estatus con GET /validator/cfdi: escancelable y estatuscancelacion te dicen en qué va.
Sí, con /invoice/cancelwithcsd. Mandas uuid, motivo, folioSustitucion, b64Cer, b64Key y passwordCSD en el body form-encoded, no en headers, porque el certificado es grande. El RFC del emisor se lee del .cer.
Sí. En el sandbox la cancelación es de prueba y la respuesta lo indica con env_mode Sandbox. Tu cuenta de sandbox es aparte, así que nada de lo que canceles ahí toca tus CFDI de producción.
Es gratis y usa la misma API v5 que producción. Cancelar no usa folio.