1. Home
  2. Servicios
  3. Timbrado V4
  4. Timbrado por Lotes — REST
  1. Home
  2. SW API´S
  3. Timbrado por Lotes — REST

Timbrado por Lotes — REST

Timbrado por Lotes — REST

Timbrado por Lotes es un servicio BATCH que realiza el timbrado masivo de comprobantes previamente sellados CFDI 4.0 en formato XML, comprimidos dentro de un archivo .zip.

El procesamiento es asíncrono, aceptar el lote responde en milisegundos y devuelve un batchId, el timbrado corre después.

En ambiente de pruebas se pueden usar certificados reales, pero recomendamos hacer uso de los CSD de pruebas.

✅ Antes de empezar

💡 Nota: Tras un periodo de inactividad, el primer lote puede demorar de 1–3 minutos en iniciar.
💡 Nota: Para utilizar este servicio, tu cuenta debe estar configurada previamente, escríbenos a soporte@sw.com.mx para apoyarte, ten en cuenta que el proceso puede demorar algo de tiempo.
🚨 Importante: Si tu cuenta no está configurada, cualquier intento de crear un lote responde 409 SETTING_MISSING.

🌐 Ambientes

Marca tu ambiente activo — los botones de copiar ruta de cada endpoint arman la URL completa con la base que elijas aquí.

🛠️ Pruebas:
🚀 Productivo:

🔄 Flujo timbrado por Lotes

  1. POST /batches201
    Crea el lote. Devuelve batchId + uploadUrl presignado.
  2. PUT <uploadUrl> (cuerpo = ZIP) → 200
    Sube el ZIP en binario directo a S3 con la URL presignada.
  3. POST /batches/{batchId}/finalize200
    Valida el ZIP, pasa el lote a Recibido y dispara el procesamiento.
  4. GET /batches/{batchId} → poll
    Consulta el estatus del lote hasta llegar a Completado.
  5. GET /batches/{batchId}/downloads200
    URLs de reportes, XML timbrados y PDFs (si aplica).

    Si tienes configurado un webhoook, recibirás esta informacición en tu endpoint.

1️⃣ Crear lote

POST /batches 📄
HeaderValor
AuthorizationBearer Token
Content-Typeapplication/json

Ejemplo request

curl --location --request POST 'https://batchstamp-smarter-rest.test.swsapien.com/v2/batchstamp/batches' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer Token' \
--data '{
    "reportConfig": {
        "variant": "both",
        "format": "XLSX"
    },
    "pdf": {
        "generate": true,
        "templateId": "cfdi40"
    }
}'

Respuesta

{
    "batchId": "86d096d2-c5f8-4e42-90b4-fa617bdcd2b1",
    "uploadUrl": "https://smarter-batch-stamp-test.s3.us-east-1.amazonaws.com/batches/2fb21d29-1716-4d6b-b01c-3d6c242b8eaf/86d096d2-c5f8-4e42-90b4-fa617bdcd2b1/input.zip?...hm=CRC32&x-amz-tagging=lifecycle%3Dpending-upload&x-id=PutObject",
    "uploadMethod": "PUT",
    "uploadHeaders": {
        "Content-Type": "application/zip"
    },
    "expiresAt": "2026-07-14T16:15:53.358Z"
}
{
    "success": false,
    "error": {
        "issues": [
            {
                "received": "XLSXX",
                "code": "invalid_enum_value",
                "options": [
                    "CSV",
                    "XLSX"
                ],
                "path": [
                    "reportConfig",
                    "format"
                ],
                "message": "Invalid enum value. Expected 'CSV' | 'XLSX', received 'XLSXX'"
            }
        ],
        "name": "ZodError"
    }
}
AtributoDescripción
batchIdIdentificador del lote. Se usa en los pasos 3, 4 y 5.
uploadUrlURL presignada (S3, PUT). Vigente 15 minutos.
uploadMethodMétodo HTTP a utilizar al subir el ZIP.
uploadHeadersHeaders que deben utilizarse al subir el ZIP (van firmados en la URL).
expiresAtMomento en que expira uploadUrl (ISO 8601).

⚙️ Opciones avanzadas del cuerpo (opcionales)

Ambas van dentro del mismo POST /batches.

📄 reportConfig

PropiedadTipoValoresDefault
variantstringerrors · complete · bothboth
formatstringCSV · XLSXXLSX

🖨️ pdf

PropiedadTipoDescripción
generateboolDefault false. Genera PDF de cada documento del lote.
templateIdstringOpcional. Si se omite, el sistema detecta la plantilla por tipo de documento (nómina, pagos, carta porte, etc.). Si se envía, esa plantilla se aplica a todos los documentos del lote.
💡 Recomendación: omite templateId salvo que todos los documentos del lote sean del mismo tipo.
Probar en Postman

2️⃣ Subir el ZIP

PUT <uploadUrl>
💡 Nota: Sin autenticación adicional — la URL ya está firmada. Envía exactamente los uploadHeaders devueltos al crear el lote.
🚨 Importante: uploadUrl expira a los 15 minutos de creado el lote. Si expira, hay que crear el lote de nuevo (paso 1).

Ejemplo request

curl --location --request PUT '' \
--header 'Content-Type: application/zip' \
--data-binary '@batch.zip'

Respuesta: 200 (directo de S3, sin cuerpo relevante).

Respuesta

Estatus 200 (directo de S3, sin cuerpo relevante).
Probar en Postman

3️⃣ Finalizar lote

POST /batches/{batchId}/finalize 📄
HeaderValor
AuthorizationBearer Token

Valida el ZIP (solo .xml, planos en la raíz, sin subcarpetas), pasa el lote a Recibido y dispara el procesamiento.

Ejemplo request

curl --location --request POST 'https://batchstamp-smarter-rest.test.swsapien.com/v2/batchstamp/batches/86d096d2-c5f8-4e42-90b4-fa617bdcd2b1/finalize' \
--header 'Authorization: Bearer Token'

Respuesta

{
    "batchId": "86d096d2-c5f8-4e42-90b4-fa617bdcd2b1",
    "status": "Recibido",
    "totalDocuments": 10,
    "metrics": {
        "processed": 0,
        "failed": 0,
        "successful": 0,
        "warnings": 0
    },
    "reportConfig": {
        "variant": "both",
        "format": "XLSX"
    },
    "createdAt": "2026-07-14T16:16:17.613Z",
    "updatedAt": "2026-07-14T16:20:36.932Z",
    "pdf": {
        "generate": true,
        "templateId": "cfdi40"
    }
}
{
    "success": false,
    "error": {
        "issues": [
            {
                "validation": "uuid",
                "code": "invalid_string",
                "message": "Invalid uuid",
                "path": [
                    "batchId"
                ]
            }
        ],
        "name": "ZodError"
    }
}
AtributoDescripción
batchIdIdentificador único del lote.
statusEstado del lote: Recibido.
totalDocumentsCantidad de XML encontrados en el ZIP.
metricsObjeto con métricas del lote.
reportConfigObjeto con la configuración de reportes del lote.
createdAtFecha de creación del lote (ISO 8601).
updatedAtFecha de última actualización del lote (ISO 8601).
pdfObjeto con la configuración de generación de PDFs del lote (si aplica).
Probar en Postman

4️⃣ Consultar estado

GET /batches/{batchId} 📄
HeaderValor
AuthorizationBearer Token

Devuelve el estado, las métricas del lote y, cuando llega a Completado, información sobre el envío de notificaciones (correo y/o webhook).

Estados posibles

EstadoSignificado
RecibidoLote finalizado, en cola para procesarse.
EnProcesoTimbrando documentos. Puede incluir el arranque en frío (ver nota al inicio del artículo).
CompletadoEl lote siempre termina aquí, incluso con documentos fallidos. Ver .
FalloCriticoFalla de plataforma, no de los datos del cliente. Debe escalarse a soporte.

Ejemplo request

curl --location --request GET 'https://batchstamp-smarter-rest.test.swsapien.com/v2/batchstamp/batches/86d096d2-c5f8-4e42-90b4-fa617bdcd2b1' \
--header 'Authorization: Bearer Token'

Respuesta

{
    "batchId": "86d096d2-c5f8-4e42-90b4-fa617bdcd2b1",
    "status": "Completado",
    "totalDocuments": 10,
    "metrics": {
        "failed": 10,
        "processed": 10,
        "successful": 0,
        "warnings": 0
    },
    "reportConfig": {
        "variant": "both",
        "format": "XLSX"
    },
    "createdAt": "2026-07-14T16:16:17.613Z",
    "updatedAt": "2026-07-14T16:21:11.385Z",
    "pdf": {
        "generate": true,
        "templateId": "cfdi40"
    },
    "notifications": {
        "email": {
            "lastAttemptedAt": "2026-07-14T16:21:10.132Z",
            "state": "Enviado"
        },
        "webhook": {
            "attemptCount": 1,
            "lastAttemptedAt": "2026-07-14T16:21:11.385Z",
            "state": "Pendiente"
        }
    }
}

Cuando status es Completado, la respuesta también incluye información del envío de notificaciones (correo y, si la cuenta tiene webhook, del intento de entrega).

{
    "success": false,
    "error": {
        "issues": [
            {
                "validation": "uuid",
                "code": "invalid_string",
                "message": "Invalid uuid",
                "path": [
                    "batchId"
                ]
            }
        ],
        "name": "ZodError"
    }
}
AtributoDescripción
batchIdIdentificador único del lote.
statusEstado del lote: Completado.
totalDocumentsCantidad de XML procesados en el ZIP.
metricsObjeto con métricas del lote.
reportConfigObjeto con la configuración de reportes del lote.
createdAtFecha de creación del lote (ISO 8601).
updatedAtFecha de última actualización del lote (ISO 8601).
pdfObjeto con la configuración de generación de PDFs del lote (si aplica).
notificationsObjeto con información de los intentos de envío de notificaciones (correo y/o webhook).
Probar en Postman

5️⃣ Descargar resultados

GET /batches/{batchId}/downloads 📄
HeaderValor
AuthorizationBearer Token
💡 Nota: Solo disponible cuando el lote está Completado. Las URLs tienen una vigencia de7 días.

Ejemplo request

curl --location --request GET 'https://batchstamp-smarter-rest.test.swsapien.com/v2/batchstamp/batches/86d096d2-c5f8-4e42-90b4-fa617bdcd2b1/downloads' \
--header 'Authorization: Bearer Token'

Respuesta

{
    "batchId": "86d096d2-c5f8-4e42-90b4-fa617bdcd2b1",
    "status": "Completado",
    "artifacts": {
        "reports": [
            {
                "kind": "errors",
                "format": "XLSX",
                "url": "https://smarter-batch-stamp-test.s3.us-east-1.amazonaws.com/batches/e8750a58-90cc-4f47-a75f-b6dc01cbadae/86d096d2-c5f8-4e42-90b4-fa617bdcd2b1/artifacts/report-errors.xlsx?X...amz-checksum-mode=ENABLED&x-id=GetObject",
                "expiresAt": "2026-07-21T16:27:42.099Z"
            },
            {
                "kind": "complete",
                "format": "XLSX",
                "url": "https://smarter-batch-stamp-test.s3.us-east-1.amazonaws.com/batches/e8750a58-90cc-4f47-a75f-b6dc01cbadae/86d096d2-c5f8-4e42-90b4-fa617bdcd2b1/artifacts/report-complete.xlsx?X...amz-checksum-mode=ENABLED&x-id=GetObject",
                "expiresAt": "2026-07-21T16:27:42.100Z"
            }
        ],
        "stampedXmls": {
            "url": "https://smarter-batch-stamp-test.s3.us-east-1.amazonaws.com/batches/e8750a58-90cc-4f47-a75f-b6dc01cbadae/86d096d2-c5f8-4e42-90b4-fa617bdcd2b1/artifacts/stamped-xmls.zip?X...amz-checksum-mode=ENABLED&x-id=GetObject",
            "expiresAt": "2026-07-21T16:27:42.101Z"
        },
        "pdfs": {
            "url": "https://smarter-batch-stamp-test.s3.us-east-1.amazonaws.com/batches/e8750a58-90cc-4f47-a75f-b6dc01cbadae/86d096d2-c5f8-4e42-90b4-fa617bdcd2b1/artifacts/pdfs.zip?X...amz-checksum-mode=ENABLED&x-id=GetObject",
            "expiresAt": "2026-07-21T16:27:42.111Z"
        }
    }
}
{
    "success": false,
    "error": {
        "issues": [
            {
                "validation": "uuid",
                "code": "invalid_string",
                "message": "Invalid uuid",
                "path": [
                    "batchId"
                ]
            }
        ],
        "name": "ZodError"
    }
}
AtributoDescripción
batchIdIdentificador único del lote.
statusEstado del lote: Completado.
artifactsObjeto con URLs de reportes, reportes, XML timbrados y PDFs (si aplica).
Probar en Postman

🧭 Catálogo de errores

Errores de API (HTTP)

HTTPcodeSignificado
401UNAUTHENTICATEDFalta el header Authorization: Bearer.
401INVALID_TOKENSW rechazó el token de la petición.
402NO_BALANCELa cuenta no tiene saldo de timbres disponible.
404BATCH_NOT_FOUNDEl lote no existe o es de otra cuenta.
409SETTING_MISSINGSe intentó crear un lote sin que la cuenta tenga alta previa.
409BATCH_NOT_READYSe pidieron las descargas antes de llegar a Completado.
400INVALID_BATCHZIP inválido al finalizar (subcarpetas o archivos no .xml). El lote se borra.
422UPLOAD_NOT_COMPLETEDfinalize llamado sin haber subido el ZIP.
422INVALID_SW_TOKENEl token guardado en la configuración de la cuenta ya no valida.
503POST_PROCESS_FAILEDEl lote quedó en FalloCritico.

Errores por documento (columna MensajeError del reporte)

CódigoTipoSignificado
ACCOUNT_TOKEN_EXPIREDErrorEl token venció a medio lote.
PARSE_ERRORErrorEl XML no se pudo interpretar.
CFDIXXXXX y otrosErrorCódigo devuelto directamente por el PAC.
UNKNOWNErrorSin clasificar.
TIMBRE_PREVIOAdvertenciaEl CFDI ya estaba timbrado. Cuenta como exitoso con advertencia, no como error — el sistema devuelve el timbre anterior y no descuenta doble ese documento.

PDF — columna EstatusPdf = Fallido:

CódigoSignificado
PDF_TIMEOUTLa generación del PDF tardó más de lo permitido.
PDF_METADATA_MISSINGFaltan datos necesarios para generar el PDF.
PDF_ENQUEUE_FAILEDNo se pudo encolar la generación del PDF.
PDF_XML_UNAVAILABLEEl XML timbrado no estaba disponible al generar el PDF.
PDF_COLLECT_ERRORFalló la recolección del PDF ya generado.
💡 Nota: Un PDF fallido nunca afecta el timbrado — el XML sí quedó timbrado y sí está en su ZIP.

📏 Reglas y límites

El lote siempre termina en Completado

Un documento fallido no tumba el lote. Llega a Completado con sus métricas (processed = successful + failed) y el detalle por documento en los reportes. FalloCritico se reserva para fallas de plataforma, no de los datos del cliente.

🚨 No reenvíes un lote a la ligera: Reenviarlo vuelve a timbrar todos los documentos — consume timbres reales y puede generar CFDIs duplicados ante el SAT. Antes de reenviar, confirma el estado real con GET /batches/{batchId}; si ya está Completado, los archivos están disponibles y no hace falta reenviar. Un CFDI ya timbrado que se reenvía sale como TIMBRE_PREVIO (advertencia, no se descuenta doble) — el riesgo real está en los documentos que aún no se habían timbrado.

Límites

LímiteValor
Documentos por lote (certificado)10,000
Tamaño del ZIP500 MB
Vigencia de las URLs de descarga7 días
Vigencia de uploadUrl15 minutos
Vida del token2 h (expira)
Arranque en frío~1–3 min tras inactividad

Tiempos de referencia: 1,000 documentos ≈ 6–8 min · 10,000 documentos ≈ 60–70 min.

Otras limitaciones

RestricciónDetalle
Re-solicitud de PDFNo existe individual — solo reenviando el lote completo (con la advertencia de arriba).
Estructura del ZIPSolo archivos .xml planos en la raíz, sin subcarpetas.
Token vencido a media corridaMarca los documentos afectados como Fallido / ACCOUNT_TOKEN_EXPIRED; el lote igual termina, se recomienda utilizar token infinito.

En SW® somos mejores para TI, es por ello que tu opinión es muy importantepor favor ayúdanos calificando este articulo y dejando tus comentarios.

How useful was this post?

Click on a star to rate it!

We are sorry that this post was not useful for you!

Let us improve this post!

Tell us how we can improve this post?

Updated on julio 14, 2026

Related Articles