The same endpoint
The REP is stamped with POST /invoice/create, just like an invoice: TipoDeComprobante "P" and the complement in Complemento.Pagos20.
When your customer pays a PPD invoice, you send the electronic payment receipt (Pagos 2.0) as JSON and get the stamped CFDI back, ultra-fast and valid with the SAT, with its XML and PDF. It's the same endpoint and the same key you use to invoice; each stamped REP uses 1 folio.
Everything a Pagos 2.0 complement carries, from your collections system or your ERP.
The REP is stamped with POST /invoice/create, just like an invoice: TipoDeComprobante "P" and the complement in Complemento.Pagos20.
Each Pago carries its DoctoRelacionado with NumParcialidad, ImpSaldoAnt, ImpPagado and ImpSaldoInsoluto. One REP can settle several invoices and record several payments.
ImpuestosDR per document, ImpuestosP per payment and the complement's Totales: IVA at 16%, 8% and 0%, exempt and withholdings.
On a REP, the API removes FormaPago and MetodoPago from the document before stamping.
When stamped, the REP is linked to the invoices in your account that it settles. GET /invoice/relations shows you that link.
With enviar_mail in the JSON, the REP is emailed to your customer as soon as it's stamped.
Your customer paid a $1,000.00 plus IVA invoice by bank transfer. The JSON goes in the json field of POST /invoice/create, with your key in the X-Api-Key header.
# FD_API=https://sandbox-app.facturadigital.com.mx/api/v5
curl -X POST "$FD_API/invoice/create" \
-H "X-Api-Key: $FD_API_KEY" \
--data-urlencode "json@recibo-de-pago.json"{
"Version": "4.0",
"Serie": "P",
"Folio": "AUTO",
"Fecha": "AUTO",
"SubTotal": "0",
"Moneda": "XXX",
"Total": "0",
"TipoDeComprobante": "P",
"Exportacion": "01",
"LugarExpedicion": "64000",
"Emisor": { "RegimenFiscal": "601" },
"Receptor": {
"Rfc": "XIA190128J61",
"Nombre": "XENON INDUSTRIAL ARTICLES",
"DomicilioFiscalReceptor": "76343",
"RegimenFiscalReceptor": "601",
"UsoCFDI": "CP01"
},
"Conceptos": [{
"ClaveProdServ": "84111506",
"Cantidad": "1",
"ClaveUnidad": "ACT",
"Descripcion": "Pago",
"ValorUnitario": "0",
"Importe": "0",
"ObjetoImp": "01"
}],
"Complemento": {
"Pagos20": {
"Totales": {
"TotalTrasladosBaseIVA16": "1000.00",
"TotalTrasladosImpuestoIVA16": "160.00",
"MontoTotalPagos": "1160.00"
},
"Pago": [{
"FechaPago": "2026-09-22T12:00:00",
"FormaDePagoP": "03",
"MonedaP": "MXN",
"TipoCambioP": "1",
"Monto": "1160.00",
"DoctoRelacionado": [{
"IdDocumento": "0B7C2E51-8A4D-4E0F-A1C3-7D9E6F2B5A10",
"Serie": "F",
"Folio": "2231",
"MonedaDR": "MXN",
"EquivalenciaDR": "1",
"NumParcialidad": "1",
"ImpSaldoAnt": "1160.00",
"ImpPagado": "1160.00",
"ImpSaldoInsoluto": "0.00",
"ObjetoImpDR": "02",
"ImpuestosDR": {
"TrasladosDR": [{
"BaseDR": "1000.00",
"ImpuestoDR": "002",
"TipoFactorDR": "Tasa",
"TasaOCuotaDR": "0.160000",
"ImporteDR": "160.00"
}]
}
}],
"ImpuestosP": {
"TrasladosP": [{
"BaseP": "1000.00",
"ImpuestoP": "002",
"TipoFactorP": "Tasa",
"TasaOCuotaP": "0.160000",
"ImporteP": "160.00"
}]
}
}]
}
},
"enviar_mail": {
"destinatarios": "cliente@facturadigital.com.mx"
}
}{
"message": "The document was generated successfully",
"code": 200,
"cfdi": {
"UUID": "6FA90F39-2D7A-4F1C-9B3E-5C1D8A0E7B42",
"FechaTimbrado": "2026-09-22T12:04:31",
"XmlBase64": "PD94bWwgdmVyc2lvbj0iMS4wIi...",
"PDF": "https://app.facturadigital.com.mx/docs/pdf/6FA90F39-...",
"XML": "https://app.facturadigital.com.mx/docs/xml/...",
"Serie": "P",
"Folio": "318",
...
}
}From the PPD invoice to the stamped REP.
The original invoice carries MetodoPago PPD and FormaPago 99. Keep its UUID: it becomes the REP's IdDocumento.
To use Folio "AUTO", create a series for document type P in Configuración → Series y Folios.
For each payment: date, payment form, amount and the documents it settles, with installment, previous balance, amount paid and outstanding balance.
Call POST /invoice/create and store the UUID, Serie and Folio from the response. With enviar_mail, the REP reaches your customer.
Errors respond with HTTP 400 and a code in the body. If the REP isn't stamped, no folio is used.
Stamp income and transfer CFDI with the Carta Porte complement, with SAT rules checked before stamping.
Cancel a REP or an invoice with reasons 01 to 04 and get the SAT acknowledgment. Cancelling uses no folio.
What the REP is, when it's issued and how to record a payment from the app.
A free test environment with the same API v5 as production.
Let your system know what happens in your account without polling the API.
Authentication, endpoints and the three ways to stamp: JSON, Base64 XML or an XML file.
Something missing? Message us or check the full reference.
POST /invoice/create, the same one you use for an invoice. You send TipoDeComprobante "P" and the complement in Complemento.Pagos20; the response is the same: UUID, Base64 XML and links to the PDF and XML.
Yes. Each stamped CFDI uses 1 folio, the REP included. The API is included: you pay for folios, not for the API, and folios never expire.
Yes. Each Pago accepts several DoctoRelacionado and the complement accepts several Pago. The amounts for the whole complement go in Pagos20.Totales.
No. You send them: ImpSaldoAnt, ImpPagado and ImpSaldoInsoluto per document, and the complement's Totales. The API builds the XML, removes FormaPago and MetodoPago, validates it against the SAT schema and stamps it.
With GET /invoice/relations and the invoice's UUID in the Uuid header. The padres list holds the CFDI that reference it, including the REPs stamped in your account.
Yes. Add enviar_mail to the JSON with destinatarios (and, optionally, bcc and mensaje) and the REP is sent as soon as it's stamped. In the sandbox, emails don't reach your customers: they're diverted to a test inbox.
Yes. Send it sealed to /invoice/stampxml (Base64) or /invoice/stampxmlfile (file). These two endpoints don't add the document to your API listings.
It's free and uses the same API v5 as production. Once it works, you switch the base URL and the key.