> ## Documentation Index
> Fetch the complete documentation index at: https://developers.ipsofactura.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Timbrar CFDI de nómina

> Genera, sella y timbra un CFDI 4.0 tipo N con complemento Nómina 1.2. Ipsofactura calcula el total como total_percepciones - total_deducciones + total_otros_pagos y valida los totales del detalle, periodo, CURP, NSS y días pagados. Es seguro reintentar con el mismo idempotency_key o con la misma serie y folio.



## OpenAPI

````yaml /openapi.json post /cfdi/timbrar-nomina
openapi: 3.1.0
info:
  description: API de timbrado CFDI 4.0 de Palenca.
  title: Ipsofactura API
  version: v1
servers:
  - description: Producción
    url: https://api.ipsofactura.com
  - description: Sandbox (pruebas, sin validez fiscal)
    url: https://sandbox.api.ipsofactura.com
security:
  - apiKeyAuth: []
tags:
  - description: Timbrado, nómina, complemento de pago, cancelación y descarga de CFDIs.
    name: CFDI
  - description: Alta y consulta de empresas emisoras.
    name: Empresas
  - description: Administración del Certificado de Sello Digital (CSD).
    name: Certificados
  - description: >-
      Suscripciones de webhooks: notificaciones de timbrado, cancelación y
      pagos.
    name: Webhooks
paths:
  /cfdi/timbrar-nomina:
    post:
      tags:
        - CFDI
      summary: Timbrar CFDI de nómina
      description: >-
        Genera, sella y timbra un CFDI 4.0 tipo N con complemento Nómina 1.2.
        Ipsofactura calcula el total como total_percepciones - total_deducciones
        + total_otros_pagos y valida los totales del detalle, periodo, CURP, NSS
        y días pagados. Es seguro reintentar con el mismo idempotency_key o con
        la misma serie y folio.
      operationId: timbrarNomina
      parameters:
        - description: >-
            Alternativa a `idempotency_key` en el cuerpo: reenviar la misma
            llave devuelve la respuesta del primer intento
            (`Idempotency-Replayed: true`) en lugar de timbrar otra vez, y la
            misma llave con un cuerpo distinto es 422 `IDEMPOTENCY_KEY_REUSED`.
            Máximo 100 caracteres ASCII imprimibles, sin espacios. Si además
            envías el campo en el cuerpo, los dos valores deben coincidir; si
            no, la respuesta es 400 `VALIDATION_ERROR`.
          in: header
          name: Idempotency-Key
          schema:
            example: nomina-emp-42-2026-09
            maxLength: 100
            type: string
      requestBody:
        content:
          application/json:
            examples:
              Nómina ordinaria:
                description: Nómina ordinaria
                value:
                  folio: 2026-001
                  idempotency_key: nomina-emp-42-2026-09
                  nomina:
                    deducciones:
                      - clave: IMSSEmp
                        concepto: Cuota IMSS empleado
                        importe: 1000
                        tipo_deduccion: '001'
                      - clave: ISR
                        concepto: ISR
                        importe: 800
                        tipo_deduccion: '002'
                    empleado:
                      curp: VECJ880326HDFXXX01
                      num_empleado: EMP-042
                      num_seguridad_social: '12345678901'
                      periodicidad_pago: '04'
                      tipo_contrato: '01'
                      tipo_jornada: '01'
                      tipo_regimen: '02'
                    empleador:
                      registro_patronal: A1234567890
                    fecha_final_pago: '2026-09-15'
                    fecha_inicial_pago: '2026-09-01'
                    fecha_pago: '2026-09-15'
                    num_dias_pagados: 15
                    percepciones:
                      - clave: '001'
                        concepto: Sueldo
                        importe_exento: 2000
                        importe_gravado: 10000
                        tipo_percepcion: '001'
                    tipo_nomina: O
                    total_deducciones: 1800
                    total_otros_pagos: 0
                    total_percepciones: 12000
                  receptor:
                    domicilio_fiscal: '06600'
                    nombre: JUAN VECTOR
                    rfc: VECJ880326XXX
                  rfc_emisor: AAA010101AAA
                  serie: NOM
            schema:
              $ref: '#/components/schemas/TimbrarNominaRequest'
        required: true
      responses:
        '201':
          content:
            application/json:
              example:
                cadena_original_sat: '||1.1|...||'
                folio: 2026-001
                id: 9a4f07ff-3abc-435f-b7a6-88c2bc65b4c1
                numero_certificado_sat: '00001000000500000000'
                sello_cfdi: ...
                sello_sat: ...
                serie: NOM
                stamped_at: '2026-09-15T10:00:00'
                uuid: 6128396f-c09b-4ec6-8699-43da5a244971
              schema:
                $ref: '#/components/schemas/TimbrarResponse'
          description: CFDI de nómina timbrado
        '400':
          content:
            application/json:
              examples:
                CURP inválida:
                  description: CURP inválida
                  value:
                    code: VALIDATION_ERROR
                    message: >-
                      La CURP del empleado debe tener 18 caracteres y un formato
                      válido
                Totales no coinciden:
                  description: Totales no coinciden
                  value:
                    code: NOMINA_TOTALS_MISMATCH
                    message: total_percepciones no coincide con la suma del detalle
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: >-
            Campo requerido, CURP/NSS, fechas o días inválidos
            (VALIDATION_ERROR), o los totales no coinciden con el detalle
            (NOMINA_TOTALS_MISMATCH)
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: >-
            API key faltante o inválida (AUTHENTICATION_REQUIRED /
            INVALID_API_KEY)
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: >-
            La cuenta está suspendida (ACCOUNT_SUSPENDED). Un recurso de otra
            cuenta NO responde 403: responde 404, igual que uno que no existe.
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: El rfc_emisor no está registrado en la cuenta (RFC_NOT_FOUND)
        '409':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: CFDI duplicado o una petición idéntica aún está en proceso
        '413':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: El cuerpo de la petición excede 4 MB
        '422':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: El idempotency_key ya se usó con un payload distinto
        '429':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: >-
            Límite de requests por minuto excedido (RATE_LIMIT_EXCEEDED). Ver
            headers X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset
            y Retry-After.
        '503':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: Servicio de timbrado no disponible
components:
  schemas:
    TimbrarNominaRequest:
      description: >-
        Solicitud para generar y timbrar un CFDI 4.0 de nómina con complemento
        Nómina 1.2.
      properties:
        fecha:
          description: 'Fecha de emisión ISO 8601. Default: fecha actual.'
          example: '2026-09-15T10:00:00'
          pattern: \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(Z|[+-]\d{2}:\d{2})?
          type: string
        folio:
          description: >-
            Folio del comprobante. Si se omite, Ipsofactura asigna el siguiente
            consecutivo.
          example: 2026-001
          maxLength: 40
          minLength: 0
          pattern: '[^|]*'
          type: string
        idempotency_key:
          description: >-
            Clave estable opcional. Reenviar la misma clave y payload devuelve
            el primer resultado.
          example: nomina-emp-42-2026-09
          maxLength: 100
          minLength: 0
          pattern: ^[\x21-\x7E]+$
          type: string
        lugar_expedicion:
          description: 'Código postal de expedición. Default: código postal del emisor.'
          example: '06600'
          type: string
        nomina:
          $ref: '#/components/schemas/NominaDTO'
          description: Datos del complemento Nómina 1.2.
        receptor:
          $ref: '#/components/schemas/ReceptorDTO'
          description: Datos fiscales del empleado receptor.
        rfc_emisor:
          description: RFC de la empresa emisora registrada en la cuenta.
          example: AAA010101AAA
          minLength: 1
          pattern: >-
            [A-Z&Ñ]{3,4}[0-9]{2}(0[1-9]|1[0-2])(0[1-9]|[12][0-9]|3[01])[A-Z0-9]{2}[0-9A]
          type: string
        serie:
          description: Serie del comprobante, máximo 25 caracteres.
          example: NOM
          maxLength: 25
          minLength: 0
          pattern: '[^|]*'
          type: string
      required:
        - nomina
        - receptor
        - rfc_emisor
      type: object
    TimbrarResponse:
      properties:
        cadena_original_sat:
          type: string
        cfdi_relacionados:
          items:
            $ref: '#/components/schemas/CfdiRelacionadosResponse'
          type: array
        fecha_timbrado:
          deprecated: true
          type: string
        folio:
          type: string
        id:
          type: string
        idempotency_key:
          description: >-
            La clave de idempotencia con la que se timbró este CFDI, si se envió
            una.
          type: string
        numero_certificado_sat:
          type: string
        sandbox:
          type: boolean
        sello_cfdi:
          type: string
        sello_sat:
          type: string
        serie:
          type: string
        stamped_at:
          type: string
        uuid:
          type: string
      type: object
    ErrorResponse:
      properties:
        code:
          description: >-
            Código de error de ipsofactura. Estable y cerrado (catálogo
            ErrorCode), pero deliberadamente grueso: TODO rechazo de validación
            del SAT llega como VALIDATION_ERROR. Úsalo para decidir el flujo
            general (reintentar, autenticar de nuevo, corregir el comprobante).
          example: VALIDATION_ERROR
          type: string
        id:
          type: string
        idempotencyKey:
          type: string
        message:
          description: >-
            Descripción legible del error, en texto libre. NO la parsees: cuando
            el rechazo viene del SAT, este texto lo redacta el PAC, cambia entre
            proveedores y puede cambiar sin previo aviso. Para ramificar por
            regla del SAT usa sat_code; para ramificar por tipo de error usa
            code. El mensaje es para mostrarlo o registrarlo.
          example: >-
            CFDI40147: El RFC del receptor no se encuentra en la lista de
            contribuyentes inscritos no cancelados del SAT
          type: string
        retryAfter:
          format: int32
          type: integer
        sat_code:
          description: >-
            Código del SAT del rechazo. Presente SÓLO cuando el rechazo viene
            del SAT a través de un PAC; null en errores propios de ipsofactura
            (validación local, autenticación, rate limit, servicio no
            disponible). Es específico donde `code` es genérico: `code` te dice
            que hubo un rechazo de validación, `sat_code` te dice exactamente
            qué rechazó el SAT. Dos formas según la operación: `CFDIxxxxx`
            cuando el SAT rechaza un timbrado, y el código de tres dígitos de
            cancelación (203 a 212) cuando rechaza una cancelación. También es
            null si el PAC no devolvió ningún código: nunca se inventa uno.
          example: CFDI40147
          type: string
    NominaDTO:
      description: Complemento Nómina 1.2.
      properties:
        deducciones:
          description: Detalle de deducciones.
          items:
            $ref: '#/components/schemas/DeduccionDTO'
          type: array
        empleado:
          $ref: '#/components/schemas/EmpleadoDTO'
          description: Datos laborales del empleado.
        empleador:
          $ref: '#/components/schemas/EmpleadorDTO'
          description: Datos del empleador para el complemento.
        fecha_final_pago:
          description: Fin del periodo pagado.
          example: '2026-09-15'
          format: date
          type: string
        fecha_inicial_pago:
          description: Inicio del periodo pagado.
          example: '2026-09-01'
          format: date
          type: string
        fecha_pago:
          description: Fecha efectiva del pago.
          example: '2026-09-15'
          format: date
          type: string
        incapacidades:
          description: Detalle de incapacidades del periodo.
          items:
            $ref: '#/components/schemas/IncapacidadDTO'
          type: array
        jubilacion_pension_retiro:
          $ref: '#/components/schemas/JubilacionPensionRetiroDTO'
          description: >-
            Datos de la jubilación, pensión o retiro. Requerido cuando alguna
            percepción tiene tipo_percepcion 039 o 044; no debe enviarse en otro
            caso.
        num_dias_pagados:
          description: Número de días pagados; debe ser mayor a cero.
          example: 15
          type: number
        otros_pagos:
          description: Detalle de otros pagos.
          items:
            $ref: '#/components/schemas/OtroPagoDTO'
          type: array
        percepciones:
          description: Detalle de percepciones; debe contener al menos una.
          items:
            $ref: '#/components/schemas/PercepcionDTO'
          minItems: 1
          type: array
        separacion_indemnizacion:
          $ref: '#/components/schemas/SeparacionIndemnizacionDTO'
          description: >-
            Datos de la separación o indemnización. Requerido cuando alguna
            percepción tiene tipo_percepcion 022, 023 o 025.
        tipo_nomina:
          description: 'Tipo de nómina: ordinaria o extraordinaria.'
          enum:
            - O
            - E
          example: O
          minLength: 1
          pattern: '[OE]'
          type: string
        total_deducciones:
          description: Total declarado de deducciones.
          example: 1800
          type: number
        total_otros_pagos:
          description: Total declarado de otros pagos.
          example: 0
          type: number
        total_percepciones:
          description: Total declarado de percepciones.
          example: 12000
          type: number
      required:
        - empleado
        - fecha_final_pago
        - fecha_inicial_pago
        - fecha_pago
        - num_dias_pagados
        - percepciones
        - tipo_nomina
        - total_deducciones
        - total_otros_pagos
        - total_percepciones
      type: object
    ReceptorDTO:
      description: Empleado que recibe la nómina.
      properties:
        domicilio_fiscal:
          description: Código postal fiscal del empleado.
          example: '06600'
          minLength: 1
          type: string
        nombre:
          description: Nombre fiscal del empleado.
          example: JUAN VECTOR
          minLength: 1
          type: string
        rfc:
          description: RFC del empleado.
          example: VECJ880326XXX
          minLength: 1
          type: string
      required:
        - domicilio_fiscal
        - nombre
        - rfc
      type: object
    CfdiRelacionadosResponse:
      description: >-
        Grupo de CFDI relacionados: el tipo de relación y los folios fiscales
        relacionados.
      properties:
        tipo_relacion:
          description: >-
            Tipo de relación (c_TipoRelacion), p. ej. 01 para una nota de
            crédito.
          type: string
        uuids:
          description: Folios fiscales (UUID) relacionados con este tipo de relación.
          items:
            type: string
          type: array
      type: object
    DeduccionDTO:
      description: Deducción de nómina.
      properties:
        clave:
          description: Clave interna de la deducción.
          example: IMSSEmp
          maxLength: 15
          minLength: 3
          type: string
        concepto:
          description: Descripción de la deducción.
          example: Cuota IMSS empleado
          minLength: 1
          type: string
        importe:
          description: Importe deducido.
          example: 1000
          type: number
        tipo_deduccion:
          description: Clave SAT de tipo de deducción.
          example: '001'
          minLength: 1
          type: string
      required:
        - clave
        - concepto
        - importe
        - tipo_deduccion
      type: object
    EmpleadoDTO:
      description: Datos laborales, salariales y bancarios del empleado.
      properties:
        antiguedad:
          description: >-
            Antigüedad laboral en formato de duración SAT: semanas completas
            (P6W) o años/meses/días terminando en días (P6Y6M14D). Si hay
            registro patronal y se omite, se calcula como el lapso entre
            fecha_inicio_rel_laboral y fecha_final_pago.
          example: P6Y6M14D
          maxLength: 10
          minLength: 0
          pattern: >-
            P(([1-9][0-9]{0,3})|0)W|P([1-9][0-9]?Y)?(([1-9]|1[012])M)?(0|[1-9]|[12][0-9]|3[01])D
          type: string
        banco:
          description: Clave SAT del banco.
          example: '006'
          maxLength: 3
          minLength: 0
          type: string
        clave_ent_fed:
          description: >-
            Clave SAT de la entidad federativa donde el empleado prestó el
            servicio.
          example: CMX
          maxLength: 3
          minLength: 0
          type: string
        cuenta_bancaria:
          description: Cuenta bancaria del empleado.
          example: 123456789012345680
          maxLength: 18
          minLength: 0
          type: string
        curp:
          description: CURP de 18 caracteres.
          example: VECJ880326HDFXXX01
          minLength: 1
          type: string
        departamento:
          description: Departamento del empleado.
          example: Tecnología
          maxLength: 100
          minLength: 0
          type: string
        fecha_inicio_rel_laboral:
          description: Fecha de inicio de la relación laboral.
          example: '2020-03-01'
          format: date
          type: string
        num_empleado:
          description: Identificador interno del empleado.
          example: EMP-042
          maxLength: 15
          minLength: 0
          type: string
        num_seguridad_social:
          description: Número de seguridad social; si se envía, debe tener 11 dígitos.
          example: 12345678901
          maxLength: 11
          minLength: 0
          type: string
        periodicidad_pago:
          description: Clave SAT de periodicidad de pago.
          example: '04'
          minLength: 1
          type: string
        puesto:
          description: Puesto del empleado.
          example: Desarrollador
          maxLength: 100
          minLength: 0
          type: string
        riesgo_puesto:
          description: Clave SAT de riesgo del puesto.
          example: 1
          type: string
        salario_base_cot_apor:
          description: Salario base de cotización y aportaciones.
          example: 500
          type: number
        salario_diario_integrado:
          description: Salario diario integrado.
          example: 550
          type: number
        tipo_contrato:
          description: Clave SAT de tipo de contrato.
          example: '01'
          minLength: 1
          type: string
        tipo_jornada:
          description: Clave SAT de tipo de jornada.
          example: '01'
          type: string
        tipo_regimen:
          description: Clave SAT de régimen del empleado.
          example: '02'
          minLength: 1
          type: string
      required:
        - clave_ent_fed
        - curp
        - num_empleado
        - periodicidad_pago
        - tipo_contrato
        - tipo_regimen
      type: object
    EmpleadorDTO:
      description: Datos patronales del emisor de la nómina.
      properties:
        registro_patronal:
          description: Registro patronal del empleador.
          example: A1234567890
          maxLength: 20
          minLength: 0
          type: string
        rfc_patron_origen:
          description: RFC del patrón origen, cuando aplica.
          example: AAA010101AAA
          maxLength: 13
          minLength: 0
          type: string
      type: object
    IncapacidadDTO:
      description: Incapacidad del periodo.
      properties:
        dias_incapacidad:
          description: Días de incapacidad.
          example: 2
          format: int32
          type: integer
        importe_monetario:
          description: Importe monetario de la incapacidad.
          example: 300
          type: number
        tipo_incapacidad:
          description: Clave SAT de tipo de incapacidad.
          example: '01'
          minLength: 1
          type: string
      required:
        - dias_incapacidad
        - tipo_incapacidad
      type: object
    JubilacionPensionRetiroDTO:
      description: >-
        Jubilación, pensión o haber de retiro (percepciones 039 y 044). Con 039
        se envía total_una_exhibicion; con 044, total_parcialidad y
        monto_diario.
      properties:
        ingreso_acumulable:
          description: Ingreso acumulable.
          example: 24000
          type: number
        ingreso_no_acumulable:
          description: Ingreso no acumulable.
          example: 126000
          type: number
        monto_diario:
          description: >-
            Monto diario de la parcialidad. Requerido con 044; no debe enviarse
            con 039.
          example: 300
          type: number
        total_parcialidad:
          description: >-
            Monto de la parcialidad. Requerido con 044; no debe enviarse con
            039.
          example: 9000
          type: number
        total_una_exhibicion:
          description: >-
            Monto pagado en una sola exhibición. Requerido con 039; no debe
            enviarse con 044.
          example: 150000
          type: number
      required:
        - ingreso_acumulable
        - ingreso_no_acumulable
      type: object
    OtroPagoDTO:
      description: Otro pago de nómina.
      properties:
        clave:
          description: Clave interna del otro pago.
          example: SUB
          maxLength: 15
          minLength: 3
          type: string
        compensacion_saldos_a_favor:
          $ref: '#/components/schemas/CompensacionSaldosAFavorDTO'
          description: >-
            Compensacion de un saldo a favor determinado en un ejercicio
            anterior.
        concepto:
          description: Descripción del otro pago.
          example: Subsidio
          minLength: 1
          type: string
        importe:
          description: Importe del otro pago.
          example: 100
          type: number
        subsidio_al_empleo:
          $ref: '#/components/schemas/SubsidioAlEmpleoDTO'
          description: >-
            Requerido cuando tipo_otro_pago es 002; no debe enviarse en ningun
            otro tipo.
        tipo_otro_pago:
          description: Clave SAT de tipo de otro pago.
          example: '002'
          minLength: 1
          type: string
      required:
        - clave
        - concepto
        - importe
        - tipo_otro_pago
      type: object
    PercepcionDTO:
      description: Percepción de nómina.
      properties:
        clave:
          description: Clave interna de la percepción.
          example: '001'
          maxLength: 15
          minLength: 3
          type: string
        concepto:
          description: Descripción de la percepción.
          example: Sueldo
          minLength: 1
          type: string
        horas_extra:
          description: >-
            Detalle de horas extra de esta percepción; sólo admitido cuando
            'tipo_percepcion' es 019.
          items:
            $ref: '#/components/schemas/HorasExtraDTO'
          type: array
        importe_exento:
          description: Importe exento.
          example: 2000
          type: number
        importe_gravado:
          description: Importe gravado.
          example: 10000
          type: number
        tipo_percepcion:
          description: Clave SAT de tipo de percepción.
          example: '001'
          minLength: 1
          type: string
      required:
        - clave
        - concepto
        - importe_exento
        - importe_gravado
        - tipo_percepcion
      type: object
    SeparacionIndemnizacionDTO:
      description: >-
        Separación o indemnización pagada en el periodo (percepciones 022, 023 y
        025).
      properties:
        ingreso_acumulable:
          description: Ingreso acumulable.
          example: 24000
          type: number
        ingreso_no_acumulable:
          description: Ingreso no acumulable.
          example: 61000
          type: number
        num_anios_servicio:
          description: Años de servicio del empleado.
          example: 6
          format: int32
          maximum: 99
          minimum: 0
          type: integer
        total_pagado:
          description: Total pagado por separación o indemnización.
          example: 85000
          type: number
        ultimo_sueldo_mens_ord:
          description: Último sueldo mensual ordinario.
          example: 24000
          type: number
      required:
        - ingreso_acumulable
        - ingreso_no_acumulable
        - num_anios_servicio
        - total_pagado
        - ultimo_sueldo_mens_ord
      type: object
    CompensacionSaldosAFavorDTO:
      description: Compensacion de saldos a favor del trabajador.
      properties:
        anio:
          description: Anio en que se determino el saldo a favor.
          example: 2025
          format: int32
          minimum: 2016
          type: integer
        remanente_sal_fav:
          description: Remanente del saldo a favor del trabajador.
          example: 10
          type: number
        saldo_a_favor:
          description: Saldo a favor determinado en periodos o ejercicios anteriores.
          example: 50
          type: number
      required:
        - anio
        - remanente_sal_fav
        - saldo_a_favor
      type: object
    SubsidioAlEmpleoDTO:
      description: Subsidio para el empleo causado en el periodo.
      properties:
        subsidio_causado:
          description: Subsidio causado conforme a la tabla del Anexo 8 de la RMF vigente.
          example: 120
          type: number
      required:
        - subsidio_causado
      type: object
    HorasExtraDTO:
      description: Horas extra pagadas.
      properties:
        dias:
          description: Días con horas extra.
          example: 1
          format: int32
          type: integer
        horas_extra:
          description: Cantidad de horas extra.
          example: 2
          format: int32
          type: integer
        importe_pagado:
          description: Importe pagado por las horas extra.
          example: 500
          type: number
        tipo_horas:
          description: Clave SAT de tipo de horas.
          example: '01'
          minLength: 1
          type: string
      required:
        - dias
        - horas_extra
        - importe_pagado
        - tipo_horas
      type: object
  securitySchemes:
    apiKeyAuth:
      description: API key emitida por Ipsofactura, enviada en el header x-api.
      in: header
      name: x-api
      type: apiKey

````