Skip to content

Payment complement API, the REP from your system

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.

What the REP API takes care of

Everything a Pagos 2.0 complement carries, from your collections system or your ERP.

The same endpoint

The REP is stamped with POST /invoice/create, just like an invoice: TipoDeComprobante "P" and the complement in Complemento.Pagos20.

Installments and several invoices

Each Pago carries its DoctoRelacionado with NumParcialidad, ImpSaldoAnt, ImpPagado and ImpSaldoInsoluto. One REP can settle several invoices and record several payments.

Payment taxes

ImpuestosDR per document, ImpuestosP per payment and the complement's Totales: IVA at 16%, 8% and 0%, exempt and withholdings.

No fields the SAT rejects

On a REP, the API removes FormaPago and MetodoPago from the document before stamping.

Linked to the invoice

When stamped, the REP is linked to the invoices in your account that it settles. GET /invoice/relations shows you that link.

Sent to your customer

With enviar_mail in the JSON, the REP is emailed to your customer as soon as it's stamped.

Paying a PPD invoice, from request to response

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.

Endpoints
POST /invoice/create
JSON with TipoDeComprobante P and Complemento.Pagos20. We build the XML, seal it with your CSD and stamp it.
GET /invoice/relations
With an invoice's UUID in the Uuid header, it returns its related CFDI, including its REPs.
POST /invoice/stampxml
If your system already builds and seals the REP XML, send it in Base64.
  • The document goes in zeros: SubTotal and Total 0, Moneda XXX and UsoCFDI CP01.
  • FormaPago and MetodoPago don't belong on a REP; if you send them, the API removes them.
  • Folio "AUTO" takes the next folio from your payments series.
  • IdDocumento is the UUID of the invoice being paid.
Production
https://app.facturadigital.com.mx/api/v5
Sandbox
https://sandbox-app.facturadigital.com.mx/api/v5
timbrar-rep.shSample data
# 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"
recibo-de-pago.jsonSample data
{
  "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"
  }
}
respuesta-200.jsonSample data
{
  "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",
    ...
  }
}

Integrate it in 4 steps

From the PPD invoice to the stamped REP.

  1. 01

    Stamp the invoice as PPD

    The original invoice carries MetodoPago PPD and FormaPago 99. Keep its UUID: it becomes the REP's IdDocumento.

  2. 02

    Set up the payments series

    To use Folio "AUTO", create a series for document type P in Configuración → Series y Folios.

  3. 03

    Build the REP for each payment

    For each payment: date, payment form, amount and the documents it settles, with installment, previous balance, amount paid and outstanding balance.

  4. 04

    Stamp and send it

    Call POST /invoice/create and store the UUID, Serie and Folio from the response. With enviar_mail, the REP reaches your customer.

Common errors and how to fix them

Errors respond with HTTP 400 and a code in the body. If the REP isn't stamped, no folio is used.

702The REP doesn't match the SAT schema: a required attribute is missing or a value doesn't follow the catalog format.Check errors: each one includes the element, the field and the message in Spanish.
400Folio "AUTO" with no payments series: “No folio range found for Serie='P' and TipoDeComprobante='P'”.Create the payments series in Configuración → Series y Folios, or send your own folio.
400The json field didn't arrive: “Invalid or non received json in headers or body.”Send the JSON in the json field of the body (application/x-www-form-urlencoded).
HTTP 400Stamping was rejected because the receiver's postal code doesn't match the SAT: “El campo Codigo Postal (DomicilioFiscalReceptor) no coincide con los registros del SAT”.Use the postal code shown on your customer's Constancia de Situación Fiscal.
601 · 602Your account has no folios left (601) or no active CSD (602).Check your balance with GET /account/balance and upload your CSD in Configuración → Certificados.
501The X-Api-Key is invalid or belongs to another environment; the message ends with [Environment: …].Use the key for the environment you're calling: sandbox and production have separate keys.
5037 · 50345037: stamping couldn't connect. 5034: stamping got no response.With 5037 you can retry. With 5034 the REP may have been stamped: look it up with the SAT using the Serie and Folio in the response before retrying.

Keep going

Carta Porte 3.1

Stamp income and transfer CFDI with the Carta Porte complement, with SAT rules checked before stamping.

Cancellation

Cancel a REP or an invoice with reasons 01 to 04 and get the SAT acknowledgment. Cancelling uses no folio.

Payment complements on the web

What the REP is, when it's issued and how to record a payment from the app.

Sandbox

A free test environment with the same API v5 as production.

Webhooks

Let your system know what happens in your account without polling the API.

API v5

Authentication, endpoints and the three ways to stamp: JSON, Base64 XML or an XML file.

REP API questions

Something missing? Message us or check the full reference.

Which endpoint do I use to stamp a payment complement?

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.

Does the REP use a folio?

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.

Can one REP settle several invoices?

Yes. Each Pago accepts several DoctoRelacionado and the complement accepts several Pago. The amounts for the whole complement go in Pagos20.Totales.

Does the API calculate balances and 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.

How do I know which REPs an invoice has?

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.

Can I email the REP to my customer?

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.

My system already builds the REP XML. Can I stamp it as is?

Yes. Send it sealed to /invoice/stampxml (Base64) or /invoice/stampxmlfile (file). These two endpoints don't add the document to your API listings.

Stamp your first REP in the sandbox

It's free and uses the same API v5 as production. Once it works, you switch the base URL and the key.