Nómina: consultas y borradores
La API permite consultar nómina, descargar recibos CFDI tipo N y crear o duplicar lotes como borradores. Revisa el borrador antes de timbrar.
Las consultas y descargas requieren payroll:read; crear y duplicar requieren payroll:write. Ambas necesitan acceso al módulo de nómina de Plan Miau. Un Despacho en modo de solo lectura conserva las consultas y descargas, pero no puede crear borradores. Los miembros sin permiso de escritura tampoco pueden crearlos. El Plan API no permite crear llaves con permisos payroll:*.
Las escrituras REST y MCP usan la misma habilitación de entorno: las credenciales live necesitan que esté habilitado MCP_LIVE_ENABLED. Las credenciales de prueba no habilitan escrituras de nómina contra el PAC de producción.
Autentica con X-API-Key. La llave consulta la cuenta de su propietario. Estas rutas también aceptan Authorization: Bearer <token> con el token OAuth de FactuCat usado por MCP. OAuth consulta el espacio seleccionado al autorizar la conexión y comprueba la membresía en cada solicitud. Un encabezado de tenant no cambia ese espacio.
Sin el permiso requerido, la respuesta es HTTP 403 con error.code: "missing_scope". Sin acceso al módulo, es HTTP 403 con error.code: "plan_required" y error.reason: "payroll_requires_miau".
Rutas y filtros
Sección titulada «Rutas y filtros»La base es /api/v1. Usa el ID de FactuCat, no el UUID fiscal del recibo.
| Método y ruta | Filtros |
|---|---|
GET /employees |
status: active o inactive; q: nombre, RFC o número de empleado |
GET /employees/{id} |
Sin filtros |
GET /payroll/batches |
status, period_from, period_to |
GET /payroll/batches/{id} |
Sin filtros; incluye los recibos del lote |
GET /payroll/receipts |
batch_id, employee_id, status, period_from, period_to, paid_from, paid_to |
GET /payroll/receipts/{id} |
Sin filtros; incluye conceptos, importes y evidencia de cancelación |
GET /payroll/receipts/{id}/xml |
Sin filtros |
GET /payroll/receipts/{id}/pdf |
Sin filtros |
Los estados de lote son draft, stamping, partially_stamped, stamped, cancelled y error. Los estados de recibo son draft, ready, stamping, stamped, error y cancelled.
Las fechas tienen formato AAAA-MM-DD. period_from limita el inicio del periodo y period_to limita su fin: el periodo completo debe quedar dentro del rango. paid_from y paid_to incluyen ambas fechas de pago. Los filtros se combinan; se aplican antes de paginar. Los recibos sin fecha de pago quedan fuera de un filtro de pago.
Respuestas y paginación
Sección titulada «Respuestas y paginación»La respuesta usa el sobre estándar: data y meta.requestId. Los tres listados aceptan limit de 1 a 100, con 50 por defecto, y cursor. La primera solicitud omite el cursor.
{ "data": [], "meta": { "requestId": "solicitud-123", "pagination": { "limit": 50, "offset": 0, "hasMore": false, "nextCursor": null } }}Cuando hasMore es true, envía nextCursor como cursor en la siguiente solicitud y conserva los filtros. El orden es por fecha de creación descendente y por ID descendente. Los cursores representan posiciones: los cambios en los datos entre solicitudes pueden desplazar las páginas.
CURP, NSS y cuenta bancaria muestran solo los últimos cuatro caracteres, por ejemplo ••••••8901. Los salarios y las percepciones siguen visibles. NSS y cuenta bancaria ausentes se devuelven como null.
Los archivos fiscales conservan sus datos completos. XML y PDF se entregan como JSON con data.fileName, data.mimeType y data.base64. Decodifica base64 para obtener el archivo.
Crear un borrador
Sección titulada «Crear un borrador»POST /payroll/batches requiere Idempotency-Key y devuelve HTTP 201 con el lote y sus recibos, igual que GET /payroll/batches/{id}. El lote queda en draft. Un recibo puede quedar en ready si tiene los datos necesarios; eso no significa que ya esté timbrado.
curl "$FACTUCAT_API_ORIGIN/api/v1/payroll/batches" \ -H "X-API-Key: $FACTUCAT_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: nomina-2026-10-segunda-quincena" \ --data '{ "periodStart": "2026-10-16", "periodEnd": "2026-10-31", "paymentDate": "2026-10-30", "payrollType": "ordinaria", "employees": [{ "employeeId": "11111111-1111-4111-8111-111111111111", "inputMode": "gross", "amount": 9000, "otherPerceptions": [{ "kind": "perception", "humanCode": "BONO", "amount": 500 }] }] }'Sustituye employeeId por el ID de un empleado activo del espacio. Las tres fechas son obligatorias. Solo se admite payrollType: "ordinaria"; puedes omitirlo. title es opcional y, si lo omites, se genera con el periodo.
Cada empleado puede indicar inputMode: "net" o "gross". Por defecto se usa net; si omites amount, se toma su salario configurado para ese modo. Un empleado inactivo, de otro espacio o sin un importe válido produce validation_error. La operación guarda el lote completo o revierte sus cambios, incluidos los folios.
Para reutilizar el último recibo timbrado de cada empleado, envía copyFromPreviousReceipt: true en el lote o en ese empleado. Se copian el modo, el importe de entrada y los conceptos adicionales. Los valores que envíes explícitamente reemplazan los copiados; una lista vacía elimina los conceptos adicionales de ese tipo. Si no existe un recibo timbrado anterior, la operación falla con validation_error.
otherPerceptions, otherDeductions y otherPayments usan los mismos conceptos del editor de borradores: kind, humanCode y amount, con satCode, description, taxableAmount, exemptAmount y editable opcionales. El kind debe ser perception, deduction u other_payment, respectivamente. Salario base, ISR, IMSS y subsidio se calculan automáticamente; no se aceptan como conceptos adicionales. Para importes o conceptos distintos por empleado, usa el cuerpo JSON o --file en la CLI.
Duplicar un lote
Sección titulada «Duplicar un lote»POST /payroll/batches/{id}/duplicate crea otro borrador con los empleados, modo, importes de entrada y conceptos adicionales de los recibos del lote de origen. Recalcula para las nuevas fechas con los datos actuales de los empleados y asigna folios nuevos.
{ "periodStart": "2026-10-16", "periodEnd": "2026-10-31", "paymentDate": "2026-10-30", "employeeIds": ["11111111-1111-4111-8111-111111111111"]}Envía también Idempotency-Key. employeeIds es opcional: omítelo para copiar todos los recibos no cancelados o úsalo para duplicar solo algunos empleados, incluso uno de régimen 02. title también es opcional.
El origen puede estar timbrado o cancelado, pero siempre se excluyen sus recibos cancelados. Seleccionar explícitamente un empleado cuyo recibo está cancelado produce validation_error. También se rechazan lotes sin recibos elegibles, empleados que no pertenecen al origen y empleados que ya están inactivos. Un lote de otra cuenta devuelve not_found.
Reintentos y revisión
Sección titulada «Reintentos y revisión»Crear y duplicar requieren una clave de idempotencia. Reutiliza la misma clave, ruta y contenido al reintentar: recibirás el mismo lote. Cambiar el contenido con esa clave devuelve HTTP 409, idempotency_conflict; si la operación sigue en curso, devuelve idempotency_in_progress. Guarda la clave antes de enviar la solicitud.
Revisa los totales, calculationWarnings, isLegallyCompleteForStamp y el status de cada recibo. Crear o duplicar no timbra ni envía recibos. El timbrado es un paso separado después de la revisión.
Crear y duplicar con MCP
Sección titulada «Crear y duplicar con MCP»Las herramientas payroll_batch_create y payroll_batch_duplicate requieren payroll:write e idempotency_key. Usan los mismos campos de los cuerpos REST; duplicar añade sourceBatchId para identificar el origen.
{ "name": "payroll_batch_duplicate", "arguments": { "sourceBatchId": "22222222-2222-4222-8222-222222222222", "periodStart": "2026-10-16", "periodEnd": "2026-10-31", "paymentDate": "2026-10-30", "employeeIds": ["11111111-1111-4111-8111-111111111111"], "idempotency_key": "duplicar-nomina-2026-10-segunda-quincena" }}La respuesta contiene el lote, recibos, totales, advertencias, estados de preparación e identificadores sensibles enmascarados. Incluye una nota para que el usuario revise el borrador. Solo después de esa revisión llama payroll_batch_stamp, que conserva su propio paso de confirmación.
El cliente de la guía del SDK expone estos métodos:
const empleados = await factucat.employees.list({ status: 'active', limit: 20 })const empleado = await factucat.employees.get(employeeId)const lotes = await factucat.payroll.batches.list({ period_from: '2026-10-01' })const lote = await factucat.payroll.batches.get(batchId)const borrador = await factucat.payroll.batches.create({ periodStart: '2026-10-16', periodEnd: '2026-10-31', paymentDate: '2026-10-30', employees: [{ employeeId, inputMode: 'gross', amount: 9000 }],}, { idempotencyKey: 'crear-quincena-octubre-2' })const copia = await factucat.payroll.batches.duplicate(batchId, { periodStart: '2026-10-16', periodEnd: '2026-10-31', paymentDate: '2026-10-30', employeeIds: [employeeId],}, { idempotencyKey: 'copiar-quincena-octubre-2' })const recibos = await factucat.payroll.receipts.list({ batch_id: batchId, status: 'stamped', paid_from: '2026-10-01', paid_to: '2026-10-31',})const recibo = await factucat.payroll.receipts.get(receiptId)const xml = await factucat.payroll.receipts.getXml(receiptId)const pdf = await factucat.payroll.receipts.getPdf(receiptId)Los listados de nómina del SDK devuelven { data, pagination }. Por ejemplo, recibos.pagination.nextCursor permite solicitar la siguiente página. Los métodos individuales devuelven el registro o archivo. El SDK genera una clave de idempotencia si la omites y la conserva en sus reintentos de red. Para reintentar la misma operación en otra llamada, pasa una clave propia.
factucat employees list --status active --q Lucía --limit 20 --jsonfactucat employees get <employeeId> --jsonfactucat payroll batches list --period-from 2026-10-01 --jsonfactucat payroll batches get <batchId> --jsonfactucat payroll batches create --period-start 2026-10-16 --period-end 2026-10-31 --payment-date 2026-10-30 --employee <employeeId> --amount 9000 --input-mode gross --jsonfactucat payroll batches create --period-start 2026-10-16 --period-end 2026-10-31 --payment-date 2026-10-30 --employee <employeeId> --copy-previous --jsonfactucat payroll batches create --file batch.json --idempotency-key crear-quincena-octubre-2 --jsonfactucat payroll batches duplicate <batchId> --period-start 2026-10-16 --period-end 2026-10-31 --payment-date 2026-10-30 --employee <employeeId> --jsonfactucat payroll receipts list --batch-id <batchId> --status stamped --jsonfactucat payroll receipts list --paid-from 2026-10-01 --paid-to 2026-10-31 --cursor 20 --limit 20 --jsonfactucat payroll receipts get <receiptId> --jsonfactucat payroll receipts xml <receiptId> --output recibo.xmlfactucat payroll receipts pdf <receiptId> --output recibo.pdf--json devuelve los datos completos de la respuesta del SDK. En descargas, --json sin --output devuelve el contenido base64 sin guardar un archivo. Con ambas opciones, guarda el archivo y añade savedPath al JSON. Sin estas opciones, guarda el documento en el directorio actual.
En creación y duplicación, --employee se puede repetir. --amount y --input-mode se aplican a todos los empleados seleccionados; --copy-previous usa sus últimos recibos timbrados. El archivo batch.json tiene el mismo cuerpo que el POST de creación y no se combina con flags de datos. Puedes añadir --title en ambos comandos.
La CLI genera --idempotency-key si no lo proporcionas y lo imprime como Idempotency-Key o como idempotencyKey en --json, incluso si falla la solicitud. Conserva ese valor para reintentar sin crear otro lote.