Todos los errores de la API usan el mismo formato JSON:
{
"error": "xml_invalido",
"message": "El nodo cfdi:Emisor no contiene el atributo RegimenFiscal.",
"status": 400,
"detail": {
"xpath": "/cfdi:Comprobante/cfdi:Emisor",
"sat_code": "CFDI33158"
}
}
| Campo | Descripción |
|---|
error | Código de error en snake_case. Úsalo para lógica en tu código. |
message | Descripción legible del error. |
status | HTTP status code. |
detail | Información adicional específica del error (no siempre presente). |
Errores por categoría
Autenticación (401 / 403)
| Código | Causa | Solución |
|---|
unauthorized | API key inválida, revocada o mal formada. | Verifica que el header sea Authorization: Bearer ipso_.... |
scope_insuficiente | Tu API key no tiene los permisos necesarios. | Contacta a soporte para revisar los permisos de tu cuenta. |
cuenta_suspendida | Tu cuenta está suspendida. | Contacta a soporte. |
XML inválido (400)
| Código | Causa | Solución |
|---|
xml_invalido | El XML no cumple el esquema CFDI 4.0 del SAT. | Revisa el campo detail.xpath para identificar el nodo problemático. |
xml_mal_formado | El XML no está bien formado (tags sin cerrar, encoding incorrecto). | Asegúrate de que el XML sea UTF-8 y los tags estén correctamente anidados. |
version_no_soportada | El XML usa una versión anterior a CFDI 4.0. | Actualiza tu generador de XML a CFDI versión 4.0. |
CSD (400)
| Código | Causa | Solución |
|---|
csd_vencido | El CSD del emisor está expirado. | Tramita un nuevo CSD ante el SAT y actualízalo en Ipsofactura. |
csd_no_encontrado | La empresa no tiene un CSD registrado. | Sube el CSD con POST /empresas/{id}/certificados. |
rfc_no_coincide | El RFC en el XML no coincide con el RFC del CSD registrado. | Verifica que el RFC del emisor sea el correcto. |
Duplicados (409 / 422)
| Código | Causa | Solución |
|---|
cfdi_duplicado | Ya existe un CFDI con la misma serie, folio y RFC. | Usa un folio diferente o consulta el CFDI existente con GET /cfdi/{uuid}. |
Saldo (402)
| Código | Causa | Solución |
|---|
saldo_insuficiente | No tienes timbres disponibles. | Contacta a soporte para recargar timbres. |
PAC (503)
| Código | Causa | Solución |
|---|
pac_no_disponible | Todos los PACs de la red están caídos. | Espera y reintenta con backoff exponencial. Consulta el status page. |
Reintentos
¿Cuándo reintentar?
| Status | ¿Reintentar? | Notas |
|---|
400 | No | Error en tu request. Corrígelo antes de reintentar. |
401 / 403 | No | Problema de credenciales. |
402 | No | Problema de saldo. |
409 / 422 | No | Error de lógica de negocio. |
429 | Sí | Demasiadas requests. Espera unos segundos y reintenta. |
500 | Sí | Error interno. Reintenta máximo 3 veces. |
503 | Sí | PAC no disponible. Usa backoff exponencial. |
Backoff exponencial recomendado
import time
import random
def timbrar_con_reintento(client, xml, max_intentos=3):
for intento in range(max_intentos):
try:
return client.cfdi.timbrar(xml=xml)
except PacNoDisponibleError:
if intento == max_intentos - 1:
raise
espera = (2 ** intento) + random.uniform(0, 1)
time.sleep(espera)
Problemas frecuentes
”Mi CFDI fue timbrado pero no lo recibo en la respuesta”
Esto puede pasar si la conexión se interrumpió después de que el PAC timbró pero antes de que la respuesta llegara a tu sistema.
Antes de reintentar el timbrado, consulta si el CFDI ya existe:
GET /cfdi?serie={serie}&folio={folio}&rfc_emisor={rfc}
Si el CFDI existe, no lo timbres de nuevo — solo descargaría el XML existente.
”Mi CSD vence pronto, ¿qué pasa si no lo renuevo?”
Los CFDIs timbrados con el CSD anterior siguen siendo válidos — el SAT no los invalida. Pero a partir de la fecha de vencimiento no podrás emitir nuevas facturas hasta subir el CSD renovado.
Monitorea el campo vigente_hasta en GET /empresas?include_certificados=true para anticiparte al vencimiento.
”El SAT rechazó mi cancelación”
El motivo más común es que el receptor rechazó activamente la cancelación antes de las 72 horas. En ese caso recibirás cancelacion_rechazada.
Opciones:
- Negocia con el receptor para que acepte la cancelación desde su portal del SAT.
- Si el CFDI tiene un error de importe, emite una nota de crédito por la diferencia.
Idempotencia
El timbrado de CFDIs es idempotente por serie + folio + RFC. Si envías el mismo XML dos veces, el segundo intento regresará cfdi_duplicado (422) en lugar de cobrar un timbre adicional.
Esto te protege contra doble cobro en caso de timeouts o reintentos.
La deduplicación se basa en la combinación serie + folio + rfc_emisor del XML. Si cambias cualquiera de estos valores, se considerará un CFDI distinto.