Todo sobre facturación CFDI
Aprende cómo automatizar y facilitar la facturación CFDI de ventas de tu negocio.
Facturas de nómina CFDI: ya se timbran desde la API de CFDI Express
Si ya integraste facturas de ingreso con la API de CFDI Express, conoces el patrón: mandas el negocio, el servidor arma el CFDI 4.0 y el SAT lo sella. Faltaba la pieza que más duele en RH y en un ERP de nómina: el recibo de nómina electrónico.
Hoy la API timbra facturas de nómina: CFDI 4.0 con TipoDeComprobante N + complemento Nómina 1.2. Un recibo por empleado por periodo. Tú mandas fechas, empleado, percepciones y deducciones. La API deriva los totales que el SAT exige y fija los valores del comprobante que no puedes “inventar”.
Esto es la API pública para cualquier sistema de nómina, RH o ERP — no es un flujo de la app de Shopify.
Por qué armar el CFDI de nómina a mano se rompe
Un recibo de nómina no es “una factura con otro uso de CFDI”. El SAT cruza el complemento Nómina 1.2 contra el comprobante: gravado y exento, ISR, descuento, subtotal, CN01, concepto 84111505, registro patronal, NSS, régimen 605. Si sumas mal una percepción o dejas FormaPago donde no va, el PAC o el SAT te regresan el XML.
Lo que suele doler si lo haces tú:
- Totales SAT.
TotalPercepciones, gravado/exento,TotalDeducciones, ISR retenido,SubTotal,DescuentoyTotaltienen que cuadrar entre sí. Un peso de diferencia tumba el timbre. - Campos que el SAT ya decidió. Tipo de comprobante
N, métodoPUE, sinFormaPago, usoCN01, un solo concepto84111505. Si los pones “como en una factura de ingreso”, fallas. - Reglas cruzadas de empleado.
registroPatronales obligatorio en contratos 01–08 y está prohibido en 09/10/99 (NOM42/43). El NSS viaja junto con el registro patronal (NOM44). El régimen del receptor en nómina es siempre 605; cualquier otro lo rechaza el SAT (NOM11). - Catálogos del complemento. Tipo de contrato, régimen de contratación, periodicidad, banco, riesgo de puesto, tipo de percepción/deducción… no son los catálogos de producto de una factura de venta.
- Un XML por persona por periodo. No hay “nómina masiva” en un solo CFDI. Si tu corrida son 400 empleados, son 400 timbres — y 400 oportunidades de duplicar si no hay idempotencia.
La API no te quita la obligación de mandar bien al empleado y las partidas. Te quita el armado SAT: totales, comprobante fijo y validación de claves.
Qué puedes hacer hoy
Misma cuenta, mismas llaves sk_test_ / sk_live_, mismo emisor (merchantId + CSD) que ya usas para facturas.
| Acción | Endpoint |
|---|---|
| Timbrar un recibo | POST /v1/nominas |
| Listar (cursor, filtros) | GET /v1/nominas |
| Consultar uno | GET /v1/nominas/{id} |
| Cancelar ante el SAT | POST /v1/nominas/{id}/cancel |
| Catálogos Nómina 1.2 | GET /v1/catalogs/nomina/{catalog} |
POST /v1/nominas espera hasta 20 s el UUID del SAT y responde 201. Si la cola está saturada, responde 202 y sigues con GET /v1/nominas/{id} (o con un webhook; más abajo). El header Idempotency-Key es obligatorio: una llave por corrida + empleado. Un replay te devuelve el recibo original (Idempotency-Replayed: true); si la misma llave sigue en vuelo, 409.
La lista filtra por merchantId, status, uuid, employeeRfc, rango de fechaPago y fechas de alta. Los estatus son stamping, stamped, stamp_failed, cancel_pending y cancelled.
Cuando el SAT ya selló, el recurso trae UUID, totales derivados y archivos (xmlUrl, pdfUrl, zipUrl) con el mismo esquema de URLs firmadas que las facturas.
Qué mandas (y qué calcula la API)
Obligatorios en NominaCreate: merchantId, fechaPago, fechaInicialPago, fechaFinalPago, numDiasPagados y employee.
Opcionales que sí usa una corrida real:
tipoNomina:Oordinaria (default) oEextraordinaria (aguinaldo, PTU, finiquito, …).folio: texto de hasta 40 caracteres para tu corrida + empleado; el folio numérico interno se asigna igual.registroPatronal: 11 caracteres (A+ 10 dígitos). Requerido en contratos 01–08; no lo mandes en 09/10/99.- Partidas:
percepciones,deducciones,otrosPagos,incapacidades. - Bloques SAT cuando aplican:
separacionIndemnizacion(percepciones 022/023/025),jubilacionPensionRetiro(039/044),entidadSNCFsi eres entidad del SNCF.
Tú no mandas TotalPercepciones, gravado/exento, TotalDeducciones, ISR, SubTotal, Descuento ni Total. La API los deriva de las partidas. Tampoco armas el nodo Comprobante: queda en N, PUE, sin FormaPago, UsoCFDI CN01 y concepto 84111505.
Cada percepción lleva tipoPercepcion, tu clave, concepto, importeGravado e importeExento. Las horas extra (019) van en horasExtra. Cada deducción lleva tipoDeduccion, clave, concepto e importe mayor que cero (NOM96). El total del recurso es el neto pagado al empleado.
El empleado: como en la Constancia, no “como en el ERP”
employee pide exactamente: rfc, name, zip, curp, tipoContrato, tipoRegimen, numEmpleado, periodicidadPago y claveEntFed.
Reglas que evitan el 400 de siempre:
name: tal cual la Constancia de Situación Fiscal — mayúsculas, sin acentos.rfc: persona física (13 caracteres). Nómina no se timbra a una moral.regimenFiscal: siempre 605 (Sueldos y Salarios). Si lo omites, la API lo asume; si mandas otro, el SAT lo rechaza (NOM11).numSeguridadSocial: 11 dígitos, junto conregistroPatronal(NOM44).antiguedad: si no la mandas, se calcula en semanas defechaInicioRelLaboralafechaFinalPago(ej.P438W).banco: obligatorio con cuenta de 10/11/16 dígitos; se omite si mandas CLABE de 18.- Opcionales de catálogo:
tipoJornada,riesgoPuesto,salarioBaseCotApor,salarioDiarioIntegrado,departamento,puesto,subcontratacion.
El curpPatron solo aplica si el emisor es persona física.
Catálogos Nómina 1.2, sin Excel del SAT
GET /v1/catalogs/nomina/{catalog} sirve un catálogo por llamada. Filtro opcional ?keyword= por clave o nombre.
Catálogos publicados: tipo-nomina, tipo-contrato, tipo-jornada, tipo-regimen, periodicidad-pago, riesgo-puesto, tipo-percepcion, tipo-deduccion, tipo-otro-pago, tipo-incapacidad, tipo-horas, origen-recurso, estado, banco.
No reutilices /v1/catalogs/products para una percepción. Son otros catálogos.
De cero a primer recibo
- Cuenta y llave
sk_test_en dash.cfdi.express. - Emisor con CSD vía
POST /v1/merchants(o el que ya tienes). - Timbrar (abajo: RFC de prueba SAT
CACX7605101P8/XOCHILT CASAS CHAVEZ; elcurpy el NSS son de ejemplo — usa los de la Constancia):
curl https://api.cfdi.express/v1/nominas \
-H "Authorization: Bearer sk_test_..." \
-H "Idempotency-Key: nomina-2026-q18-0042" \
-H "Content-Type: application/json" \
-d '{
"merchantId": "mer_...",
"tipoNomina": "O",
"fechaPago": "2026-09-15",
"fechaInicialPago": "2026-09-01",
"fechaFinalPago": "2026-09-15",
"numDiasPagados": 15,
"registroPatronal": "B5510768108",
"employee": {
"rfc": "CACX7605101P8",
"name": "XOCHILT CASAS CHAVEZ",
"zip": "36257",
"curp": "CACX760510MGTSSL09",
"numSeguridadSocial": "12345678901",
"tipoContrato": "01",
"tipoRegimen": "02",
"numEmpleado": "0042",
"periodicidadPago": "04",
"claveEntFed": "GTO"
},
"percepciones": [{
"tipoPercepcion": "001",
"clave": "SUE",
"concepto": "Sueldo quincenal",
"importeGravado": 12000,
"importeExento": 0
}],
"deducciones": [{
"tipoDeduccion": "002",
"clave": "ISR",
"concepto": "ISR retenido",
"importe": 1450
}]
}'
- Si llega 201, guarda
uuidy las URLs de XML/PDF/ZIP. Si llega 202, consultaGET /v1/nominas/{id}o espera el webhook.
Los valores de catálogo (01, 02, 04, 001, 002) salen de GET /v1/catalogs/nomina/.... No los copies de un Excel viejo.
Cancelación y webhooks
POST /v1/nominas/{id}/cancel también exige Idempotency-Key. El body es el mismo CancelRequest de facturas: motivo 01–04 y folioSustitucion cuando el motivo es 01. Respuesta 202 con estatus y acuse en el recurso.
Si no quieres pollar el 202 ni el acuse, suscríbete a nomina.stamped, nomina.stamp_failed y nomina.cancelled. El flujo de endpoints, whsec_ y CFDI-Signature está en Webhooks CFDI: notificaciones en tiempo real.
Precio: el mismo saldo, un timbre por recibo
OpenAPI no publica una tarifa aparte para nómina. Un recibo consume el mismo saldo prepagado que una factura de ingreso (GET /v1/balance expone pricePerTimbreCentavos y timbresRemaining). Si no hay créditos, POST /v1/nominas responde 402.
En la landing de la API el precio publicado sigue siendo $1 MXN por timbre en producción, con descuento por volumen, sandbox ilimitado con sk_test_ y recargas de $100 a $50,000 MXN por Stripe. Un empleado × un periodo = un timbre. La Idempotency-Key evita que un retry te cobre el doble.
Empieza hoy
- Abre las docs interactivas y prueba
POST /v1/nominasconsk_test_. - Crea (o entra a) tu cuenta en dash.cfdi.express.
- Si tu nómina ya corre en un ERP o un motor de RH, apunta la corrida a un recibo por empleado y deja los totales al servidor.
¿Aún no tienes la API? El contexto está en Lanzamos CFDI Express API. ¿Quieres verlo en una llamada? Agenda una demo o escribe a hola@cfdi.express.
El SAT no va a simplificar Nómina 1.2. Tu integrador de nómina sí puede dejar de calcularlo a mano.
