Skip to main content
POST /cfdi/complemento-pago timbra un CFDI tipo P (Recibo Electrónico de Pago, o REP) contra una o varias facturas PPD que ya timbraste con Ipsofactura. Cada elemento de pagos[].documentos_relacionados apunta a la factura que ese pago liquida.

id_documento y CFDI de otro PAC

Un documento relacionado se referencia de una de dos formas, y sólo una es necesaria:
Un pago que liquida una PPD por folio fiscal
Si no envías ninguno de los dos, la respuesta es 400 VALIDATION_ERROR (“Cada documento relacionado requiere factura_id o id_documento”). Si id_documento no es un UUID, también 400 VALIDATION_ERROR.

Sólo facturas timbradas con Ipsofactura

id_documento no es un folio fiscal cualquiera: se busca entre los CFDIs de tu cuenta. Un folio fiscal que no timbramos nosotros no existe para este endpoint y la respuesta es 400 VALIDATION_ERROR (“CFDI relacionado no encontrado: …”).
Un complemento de pago sólo puede aplicarse a una factura que timbraste a través de Ipsofactura. Es la misma regla que la cancelación: operamos sobre los CFDIs que están en tu cuenta, porque el complemento necesita el documento —su total, su moneda y su receptor— para armar el REP, y su histórico de pagos para derivar num_parcialidad, imp_saldo_ant e imp_saldo_insoluto. Ese histórico no existe para un CFDI emitido en otra plataforma, y mandar los saldos explícitos no levanta la regla: seguimos necesitando el documento para lo demás.
Además de existir en tu cuenta, la factura relacionada debe cumplir (si no, 400 VALIDATION_ERROR con el detalle en message):
  • ser de tipo_comprobante I o E;
  • tener metodo_pago PPD — una PUE no lleva complemento;
  • estar en estatus timbrado;
  • tener el mismo RFC emisor que el complemento.
Esto se diferencia de cfdi_relacionados al timbrar (nodo CfdiRelacionados), donde sí puedes declarar folios fiscales que no pasaron por Ipsofactura: ahí el UUID viaja tal cual al XML y el SAT lo valida. En un complemento de pago no basta con arrastrar el UUID: hay que calcular saldos.

Migración desde otro PAC

Si llegas a Ipsofactura con facturas PPD abiertas timbradas con tu proveedor anterior, esos saldos no se migran. Las opciones son dos:
  1. Liquidarlas en el proveedor anterior: timbra ahí los complementos de pago que falten, y usa Ipsofactura para las facturas nuevas.
  2. Refacturar: cancela la PPD abierta y vuelve a emitirla con Ipsofactura. La cancelación sí la puedes hacer desde aquí, por folio_fiscal — ver Cancelar un CFDI timbrado con otro proveedor y Refacturación.
No existe una importación de CFDIs externos en esta versión de la API. Si tu operación la necesita, escríbenos: sería una funcionalidad aparte, y depende de poder declarar saldos iniciales explícitos sobre un documento que nosotros no timbramos.

Parcialidad y saldos: derivados o explícitos

Por omisión no mandas num_parcialidad, imp_saldo_ant ni imp_saldo_insoluto: los derivamos del histórico de pagos que ya timbramos contra esa factura. El primer pago arranca del total de la factura y cada siguiente arrastra el imp_saldo_insoluto del anterior. Si tu sistema no lleva contabilidad de saldos, no tienes que hacer nada — este es el camino de siempre y no cambió. Si tu sistema lleva su propia contabilidad, puedes declararlos por documento relacionado y entonces gana lo que mandas:
Un pago que declara su propia parcialidad y saldos

Qué validamos

Puedes mandar los tres, algunos o ninguno; el que omitas se deriva. Lo que no puedes mandar es una cadena incoherente, y son tres reglas — todas 400 VALIDATION_ERROR nombrando el campo, sin consumir timbre:
  • La identidad, con tolerancia cero: imp_saldo_ant − imp_pagado = imp_saldo_insoluto. Se verifica sobre los valores a dos decimales que realmente vamos a timbrar, así que una request que aceptamos no puede después ser rechazada por el SAT (CRP20244) por un redondeo nuestro.
  • num_parcialidad monótona: tiene que ser mayor a la última que timbramos para esa factura. Un hueco sí se permite — tu contabilidad puede contar parcialidades que nosotros nunca timbramos, por ejemplo pagos registrados fuera de Ipsofactura. Repetir o ir hacia atrás no, porque dejaría dos REPs reclamando la misma parcialidad del mismo documento.
  • serie_dr / folio_dr no pueden contradecir: sólo se aceptan si la factura resuelta no trae serie o folio propios, o si coinciden. Un valor distinto timbraría un REP apuntando a una factura que no existe bajo esa serie y folio.
Estos campos son de POST /cfdi/complemento-pago. En timbrado por XML la parcialidad y el saldo se siguen recalculando a partir de nuestro histórico, no de lo que el XML declare.

Egreso aplicado antes del REP

Si aplicaste una nota de crédito (CFDI tipo E) contra la PPD antes de timbrar el complemento, el saldo que tú traes ya viene disminuido y el nuestro no: nosotros derivamos del histórico de pagos, y un egreso no es un pago. Ahí es donde imp_saldo_ant explícito importa — mándalo con el saldo real después del egreso y el REP se timbra con la cadena correcta. Sin él, derivaríamos el saldo previo al egreso y la suma que verías en el XML no cuadraría con tu contabilidad.

Límite del periodo de prueba

Un complemento de pago consume un timbre igual que una factura, así que el límite de tu periodo de prueba también se aplica aquí: agotado el cupo, POST /cfdi/complemento-pago responde 402 TRIAL_LIMIT_EXCEEDED antes de resolver nada del request y sin llamar al PAC. Es el mismo código y el mismo mensaje que en POST /cfdi/timbrar.

Si el timbrado del REP no responde

POST /cfdi/complemento-pago tiene la misma red de seguridad que el timbrado de facturas. Cuando el PAC no confirma a tiempo, la respuesta es 503 STAMPING_OUTCOME_UNKNOWN con retry_after, y el REP no se pierde:
  1. Ipsofactura vuelve a preguntar por ese timbrado en segundo plano.
  2. Cuando el resultado se confirma, el complemento queda registrado completo —su árbol de pagos y documentos relacionados incluidos— y se emite el webhook pago.timbrado tardío, idéntico al de un REP timbrado a tiempo.
  3. Un reintento con la misma idempotency_key responde 409 DUPLICATE_IN_PROGRESS mientras la reconciliación sigue pendiente, y el 201 original del complemento —con sus documentos_relacionados— en cuanto se resuelve.
Esto importa más en un REP que en una factura: la cadena de parcialidades de la PPD se deriva de los pagos que registramos, así que un REP perdido dejaría cada parcialidad posterior sobre un saldo equivocado. El patrón completo está en Recuperación tras timeout.
Igual que en el timbrado de facturas: no reintentes con otra idempotency_key. Un timeout no es evidencia de que no se timbró.

rfc_receptor: sólo para el sandbox

El receptor de un CFDI tipo P lo determina la factura que liquida —el SAT exige que coincidan—, así que el REP nunca lleva un receptor propio. Por eso el campo opcional rfc_receptor de POST /cfdi/complemento-pago no se timbra: existe para que en el sandbox puedas forzar un escenario con los mismos RFC reservados que usas en POST /cfdi/timbrar. En producción se ignora.
Forzar un escenario del sandbox en el REP