Skip to main content
POST
Register a payment of PPD invoices and stamp its Complemento de Pago (CFDI tipo P)
Records a payment of one or more PPD invoices (pago en parcialidades o diferido) and stamps its Complemento de Pago 2.0 (CFDI type P, also called REP). The SAT requires one every time you receive a payment for a PPD invoice. You only send which invoices were paid and how much. Timbrix computes the installment number (NumParcialidad), the previous and remaining balances, and the proportional taxes of each invoice from its stamped XML.

The PPD → payment flow

  1. Stamp the invoice with paymentMethod: "PPD" and paymentForm: "99" (Create Invoice). Its balance starts at the invoice total.
  2. Each time the customer pays, call this endpoint. Every call stamps one Complemento de Pago and lowers the balance.
  3. Check the installments and balance with Get Payments.

Authentication

Same as Create Invoice: a Supabase session with X-Organization-Id, or an API key with the write:invoices scope (the organization and environment come from the key).

Request Body

Each Complemento de Pago counts as one CFDI of your plan (or of the sandbox daily limit).
Behavior change (series and folios). A series sent without folioNumber must exist in the organization: otherwise the request now returns 404 SERIES_NOT_FOUND. Before, Timbrix stamped with that series and the count of payments plus one as the folio. Create the series first with Create series, or omit series to use the default for type P.
Automatic folios skip numbers already used in the series (by any CFDI, including cancelled ones, or by a stamp in progress). A stamp that fails still uses its automatic folio, so the series can have a gap. The SAT does not require consecutive folios.

Bank details

The SAT c_FormaPago catalog decides which bank details a payment form accepts and the format of each account. Timbrix checks them before stamping and returns 400 with a clear message instead of a SAT rejection. Spaces around the values are removed and the bank RFCs are uppercased; accounts are sent as written. “Card or account” means 10, 11, 15, 16 or 18 digits, or 10 to 50 uppercase letters, digits or underscores. payerBankRfc and payeeBankRfc must be the 12-character RFC of a Mexican bank. Foreign banks (XEXX010101000) are not supported yet: leave payerBankRfc out.

Example

Response

The same shape as any invoice (type: "P", total: 0), plus a payment block:
If the customer’s RFC appears as Presunto in the SAT’s 69-B list, the complement is stamped anyway and the response adds warnings: [{ "code": "RFC_69B_PRESUNTO", "rfc": "…", "role": "receptor", "message": "…" }] (only present when there are warnings; a retry with the same idempotencyKey returns the original response without new ones). If it appears as Definitivo and every paid invoice was stamped to that same RFC, the complement is also stamped, because it complements CFDI already issued, and the warning has code RFC_69B_DEFINITIVO. See Lista 69-B del SAT.

Errors

Other request validation errors (paymentForm 99, more than 2 decimals, 1 to 50 documents) return 400. Two payments sent at the same time for the same invoice never exceed its balance: the second one sees the first one while it is being stamped.

Credit notes

A vigente credit note (Egreso related to the invoice with TipoRelacion 01) reduces the invoice balance: balance = total − credit notes − payments. A PPD invoice of 1160.00 with a credit note of 160.00 has a balance of 1000.00, and a payment of 1000.00 is stamped with NumParcialidad 1, ImpSaldoAnt 1000.00 and ImpSaldoInsoluto 0.00, as in the SAT’s filling guide. The ImpuestosDR are prorated over what is left of the invoice: factor = ImpPagado / (Total − credit notes), applied to the invoice taxes minus the credit notes’ taxes (by tax, factor type and rate). If a credit note has a tax or rate the invoice does not have, the payment returns 422 (taxes cannot be computed). Cancelling a credit note puts its amount back in the balance. If payments were registered after the credit note, cancel those Complementos de Pago first and register them again, so each one’s balances stay consistent. While a credit note for the invoice is being stamped, a payment for that invoice returns 409 (“La factura tiene una nota de crédito en timbrado; reintenta en unos minutos”): its amount and taxes are not confirmed yet. Retry once the credit note is stamped (or confirmed as not stamped). A credit note and a payment sent at the same time for the same invoice never exceed its balance together.

Webhooks

Each Complemento de Pago sends invoice.stamped (with tipoComprobante: "P") and invoice.payment_received (payload documented there).

Cancelling

A Complemento de Pago is cancelled directly (no receptor approval) with Cancel Invoice. Its amount goes back to the invoice balance. To fix one, replace it (below) and cancel it with motivo 01; motivo 02 cancels it without a replacement. To cancel a PPD invoice, first cancel its Complementos de Pago.

Replacing a Complemento de Pago

To correct a Complemento de Pago (A), stamp its replacement (B) with substitutes set to A’s UUID, then cancel A with motivo 01 and folioSustitucion set to B’s UUID. B carries CfdiRelacionados with TipoRelacion 04 pointing at A, and relatedCfdis in the response shows it.
  • B must pay the same invoices as A. Payment date, payment form, bank details and the receptor can change.
  • Each invoice keeps the NumParcialidad it had in A.
  • If A is the invoice’s last installment that counts, ImpSaldoAnt is recomputed from the current balance (credit notes included) and the amount can be corrected.
  • If later installments exist, B keeps A’s ImpSaldoAnt and must pay the same amount, so the balances of the later installments stay consistent.
  • While B is vigente or being stamped, A no longer counts against the balance, even before you cancel it. If B fails or is cancelled, A counts again. Get Invoice Payments shows substitutedByUuid on A.
  • A can also be already cancelled (for example with motivo 02): B reissues that installment with its original number. If another vigente Complemento de Pago already uses that NumParcialidad for the invoice, the request returns 409.
  • Cancel A with motivo 01 and folioSustitucion = B. Until then both are vigente at the SAT.

Authorizations

X-API-Key
string
header
required

API Key for authentication (format: sk_...)

Headers

x-timbrix-client
string
required
X-Timbrix-Client
string

Canal de origen del CFDI: web, api, sdk, cli o mcp.

X-Organization-Id
string

Required for Supabase session auth. Ignored when authenticating with an API key (the organization resolves from the key).

Body

application/json
date
string
required

Fecha y hora del pago en la Ciudad de México (sin zona horaria)

Example:

"2026-10-15T12:00:00"

paymentForm
string
required

Forma de pago del catálogo c_FormaPago del SAT (sin 99)

Example:

"03"

documents
object[]
required

Facturas PPD que liquida este pago (1 a 50)

customerId
string

Cliente registrado. Si se omite, el receptor del complemento sale de la primera factura. Si se envía, su RFC debe ser el de todas las facturas.

currency
string

Default: MXN. Debe ser la moneda de las facturas.

Example:

"MXN"

exchange
number

Tipo de cambio a MXN. Obligatorio si currency no es MXN; se ignora con MXN.

Example:

18.5

operationNumber
string

Número o referencia de la operación

Example:

"SPEI-123"

payerBankRfc
string

RFC del banco del que paga

payerAccount
string

Cuenta del que paga

payeeBankRfc
string

RFC del banco que recibe

payeeAccount
string

Cuenta que recibe

series
string

Serie del complemento. Default: P

Example:

"P"

folioNumber
string

Folio del complemento. Si se omite, Timbrix usa el siguiente de la organización.

Example:

"12"

substitutes
string

Folio fiscal (UUID) del complemento de pago que este sustituye (CfdiRelacionados 04). Debe liquidar las mismas facturas; cada una conserva su número de parcialidad. Si el original sigue vigente, cancélalo después con motivo 01.

Example:

"5f0e7c4b-2a8d-4c1e-9b3a-7d6f1e2c3b4a"

idempotencyKey
string

Llave para reintentar sin timbrar dos veces el mismo complemento

Example:

"pago-8421"

Response

id
string
required

Timbrix invoice record ID

Example:

"0f2a1c3e-1a2b-4c3d-9e8f-1234567890ab"

uuid
string
required

Folio fiscal UUID asignado por el SAT vía PAC

Example:

"d3bfbc57-44af-4390-a064-f0afab85e5df"

status
enum<string>
required

Estado fiscal del CFDI registrado en Timbrix: vigente o cancelado. Antes de TIM-243 este campo era siempre "valid". Con una cancelación pendiente (cancellationStatus: pendiente) cuyo plazo de respuesta del receptor ya venció, el SAT pudo haberlo cancelado automáticamente; confírmalo en el portal del SAT.

Available options:
vigente,
cancelado
Example:

"vigente"

cancellationStatus
enum<string> | null
required

Latest cancellation attempt's status, only while the invoice is still vigente. null if never attempted or already cancelado. A pendiente past the receptor's response deadline may already be cancelado at the SAT. en_proceso: the cancellation was sent and its outcome is being confirmed with the SAT; don't treat the CFDI as vigente yet.

Available options:
pendiente,
rechazada,
en_proceso
cancellationDesynced
boolean
required

true when a cancellation was accepted but the invoice status update failed to apply — indicates the invoice may already be cancelled at the SAT even though it still shows vigente here; needs manual reconciliation.

Example:

false

type
enum<string>
required

I = Ingreso, E = Egreso, T = Traslado, P = Pago

Available options:
I,
E,
T,
P
Example:

"I"

series
string
required
Example:

"A"

folioNumber
string
required
Example:

"1"

total
number
required
Example:

116

date
string
required
Example:

"2026-07-30T22:50:00"

xml
string
required

XML del CFDI timbrado (UTF-8)

createdAt
string
required
Example:

"2026-07-30T22:50:03.412Z"

environment
enum<string>
required

Ambiente en el que se timbró el CFDI. Los CFDI de sandbox no tienen validez fiscal ante el SAT.

Available options:
sandbox,
production
Example:

"production"

channel
enum<string> | null
required

Canal de origen declarado por el cliente con el header X-Timbrix-Client (api si no se envió). null para CFDI anteriores a este campo.

Available options:
web,
api,
sdk,
cli,
mcp
Example:

"mcp"

rfcReceptor
string
required

RFC del receptor del CFDI

Example:

"GODE561231GR8"

Relaciones de este CFDI (CfdisRelacionados). Con tipo 04 apunta al CFDI que sustituye.

substitutedBy
object[]
required

CFDI cuya relación 04 apunta a este comprobante (sustitutos emitidos).

paymentMethod
enum<string> | null
required

Método de pago; null en Traslado, Pago y facturas anteriores a este campo.

Available options:
PUE,
PPD
Example:

"PPD"

balance
object | null
required

Saldo pendiente de una factura PPD (total menos complementos de pago vigentes o en timbrado); null en las demás.

Example:

160

payment
object

Solo en complementos de pago (type P): el pago y las facturas que liquida.