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, Descuento y Total tienen que cuadrar entre sí. Un peso de diferencia tumba el timbre.
  • Campos que el SAT ya decidió. Tipo de comprobante N, método PUE, sin FormaPago, uso CN01, un solo concepto 84111505. Si los pones “como en una factura de ingreso”, fallas.
  • Reglas cruzadas de empleado. registroPatronal es 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: O ordinaria (default) o E extraordinaria (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), entidadSNCF si 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 con registroPatronal (NOM44).
  • antiguedad: si no la mandas, se calcula en semanas de fechaInicioRelLaboral a fechaFinalPago (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

  1. Cuenta y llave sk_test_ en dash.cfdi.express.
  2. Emisor con CSD vía POST /v1/merchants (o el que ya tienes).
  3. Timbrar (abajo: RFC de prueba SAT CACX7605101P8 / XOCHILT CASAS CHAVEZ; el curp y 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
    }]
  }'
  1. Si llega 201, guarda uuid y las URLs de XML/PDF/ZIP. Si llega 202, consulta GET /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

  1. Abre las docs interactivas y prueba POST /v1/nominas con sk_test_.
  2. Crea (o entra a) tu cuenta en dash.cfdi.express.
  3. 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.