curl --request POST \
--url https://api.ipsofactura.com/cfdi/complemento-pago \
--header 'Content-Type: application/json' \
--header 'x-api: <api-key>' \
--data '
{
"pagos": [
{
"documentos_relacionados": [
{
"factura_id": "<id-del-CFDI-PPD>",
"imp_pagado": 1160
}
],
"fecha_pago": "2026-07-21T09:55:00",
"forma_pago_p": "03",
"moneda_p": "MXN",
"monto": 1160,
"num_operacion": "OP-0001"
}
],
"rfc_emisor": "EKU9003173C9"
}
'import requests
url = "https://api.ipsofactura.com/cfdi/complemento-pago"
payload = {
"pagos": [
{
"documentos_relacionados": [
{
"factura_id": "<id-del-CFDI-PPD>",
"imp_pagado": 1160
}
],
"fecha_pago": "2026-07-21T09:55:00",
"forma_pago_p": "03",
"moneda_p": "MXN",
"monto": 1160,
"num_operacion": "OP-0001"
}
],
"rfc_emisor": "EKU9003173C9"
}
headers = {
"x-api": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'x-api': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
pagos: [
{
documentos_relacionados: [{factura_id: '<id-del-CFDI-PPD>', imp_pagado: 1160}],
fecha_pago: '2026-07-21T09:55:00',
forma_pago_p: '03',
moneda_p: 'MXN',
monto: 1160,
num_operacion: 'OP-0001'
}
],
rfc_emisor: 'EKU9003173C9'
})
};
fetch('https://api.ipsofactura.com/cfdi/complemento-pago', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.ipsofactura.com/cfdi/complemento-pago",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'pagos' => [
[
'documentos_relacionados' => [
[
'factura_id' => '<id-del-CFDI-PPD>',
'imp_pagado' => 1160
]
],
'fecha_pago' => '2026-07-21T09:55:00',
'forma_pago_p' => '03',
'moneda_p' => 'MXN',
'monto' => 1160,
'num_operacion' => 'OP-0001'
]
],
'rfc_emisor' => 'EKU9003173C9'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"x-api: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.ipsofactura.com/cfdi/complemento-pago"
payload := strings.NewReader("{\n \"pagos\": [\n {\n \"documentos_relacionados\": [\n {\n \"factura_id\": \"<id-del-CFDI-PPD>\",\n \"imp_pagado\": 1160\n }\n ],\n \"fecha_pago\": \"2026-07-21T09:55:00\",\n \"forma_pago_p\": \"03\",\n \"moneda_p\": \"MXN\",\n \"monto\": 1160,\n \"num_operacion\": \"OP-0001\"\n }\n ],\n \"rfc_emisor\": \"EKU9003173C9\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-api", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.ipsofactura.com/cfdi/complemento-pago")
.header("x-api", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"pagos\": [\n {\n \"documentos_relacionados\": [\n {\n \"factura_id\": \"<id-del-CFDI-PPD>\",\n \"imp_pagado\": 1160\n }\n ],\n \"fecha_pago\": \"2026-07-21T09:55:00\",\n \"forma_pago_p\": \"03\",\n \"moneda_p\": \"MXN\",\n \"monto\": 1160,\n \"num_operacion\": \"OP-0001\"\n }\n ],\n \"rfc_emisor\": \"EKU9003173C9\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.ipsofactura.com/cfdi/complemento-pago")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-api"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"pagos\": [\n {\n \"documentos_relacionados\": [\n {\n \"factura_id\": \"<id-del-CFDI-PPD>\",\n \"imp_pagado\": 1160\n }\n ],\n \"fecha_pago\": \"2026-07-21T09:55:00\",\n \"forma_pago_p\": \"03\",\n \"moneda_p\": \"MXN\",\n \"monto\": 1160,\n \"num_operacion\": \"OP-0001\"\n }\n ],\n \"rfc_emisor\": \"EKU9003173C9\"\n}"
response = http.request(request)
puts response.read_body{
"documentos_relacionados": [
{
"id_documento": "0bcfae68-...",
"imp_pagado": "1160.00",
"imp_saldo_insoluto": "0.00",
"liquidado": true,
"num_parcialidad": 1
}
],
"fecha_timbrado": "2026-07-21T09:56:00",
"id": "169fe189-...",
"sello_cfdi": "...",
"sello_sat": "...",
"uuid": "b0440bb7-..."
}{
"code": "VALIDATION_ERROR",
"id": "<string>",
"idempotencyKey": "<string>",
"message": "CFDI40147: El RFC del receptor no se encuentra en la lista de contribuyentes inscritos no cancelados del SAT",
"retryAfter": 123,
"sat_code": "CFDI40147"
}{
"code": "VALIDATION_ERROR",
"id": "<string>",
"idempotencyKey": "<string>",
"message": "CFDI40147: El RFC del receptor no se encuentra en la lista de contribuyentes inscritos no cancelados del SAT",
"retryAfter": 123,
"sat_code": "CFDI40147"
}{
"code": "VALIDATION_ERROR",
"id": "<string>",
"idempotencyKey": "<string>",
"message": "CFDI40147: El RFC del receptor no se encuentra en la lista de contribuyentes inscritos no cancelados del SAT",
"retryAfter": 123,
"sat_code": "CFDI40147"
}{
"code": "VALIDATION_ERROR",
"id": "<string>",
"idempotencyKey": "<string>",
"message": "CFDI40147: El RFC del receptor no se encuentra en la lista de contribuyentes inscritos no cancelados del SAT",
"retryAfter": 123,
"sat_code": "CFDI40147"
}{
"code": "VALIDATION_ERROR",
"id": "<string>",
"idempotencyKey": "<string>",
"message": "CFDI40147: El RFC del receptor no se encuentra en la lista de contribuyentes inscritos no cancelados del SAT",
"retryAfter": 123,
"sat_code": "CFDI40147"
}Complemento de pago
Registra un pago (CFDI tipo P) contra un CFDI PPD previamente timbrado. El CFDI referenciado debe existir, ser PPD y del mismo emisor.
curl --request POST \
--url https://api.ipsofactura.com/cfdi/complemento-pago \
--header 'Content-Type: application/json' \
--header 'x-api: <api-key>' \
--data '
{
"pagos": [
{
"documentos_relacionados": [
{
"factura_id": "<id-del-CFDI-PPD>",
"imp_pagado": 1160
}
],
"fecha_pago": "2026-07-21T09:55:00",
"forma_pago_p": "03",
"moneda_p": "MXN",
"monto": 1160,
"num_operacion": "OP-0001"
}
],
"rfc_emisor": "EKU9003173C9"
}
'import requests
url = "https://api.ipsofactura.com/cfdi/complemento-pago"
payload = {
"pagos": [
{
"documentos_relacionados": [
{
"factura_id": "<id-del-CFDI-PPD>",
"imp_pagado": 1160
}
],
"fecha_pago": "2026-07-21T09:55:00",
"forma_pago_p": "03",
"moneda_p": "MXN",
"monto": 1160,
"num_operacion": "OP-0001"
}
],
"rfc_emisor": "EKU9003173C9"
}
headers = {
"x-api": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'x-api': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
pagos: [
{
documentos_relacionados: [{factura_id: '<id-del-CFDI-PPD>', imp_pagado: 1160}],
fecha_pago: '2026-07-21T09:55:00',
forma_pago_p: '03',
moneda_p: 'MXN',
monto: 1160,
num_operacion: 'OP-0001'
}
],
rfc_emisor: 'EKU9003173C9'
})
};
fetch('https://api.ipsofactura.com/cfdi/complemento-pago', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.ipsofactura.com/cfdi/complemento-pago",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'pagos' => [
[
'documentos_relacionados' => [
[
'factura_id' => '<id-del-CFDI-PPD>',
'imp_pagado' => 1160
]
],
'fecha_pago' => '2026-07-21T09:55:00',
'forma_pago_p' => '03',
'moneda_p' => 'MXN',
'monto' => 1160,
'num_operacion' => 'OP-0001'
]
],
'rfc_emisor' => 'EKU9003173C9'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"x-api: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.ipsofactura.com/cfdi/complemento-pago"
payload := strings.NewReader("{\n \"pagos\": [\n {\n \"documentos_relacionados\": [\n {\n \"factura_id\": \"<id-del-CFDI-PPD>\",\n \"imp_pagado\": 1160\n }\n ],\n \"fecha_pago\": \"2026-07-21T09:55:00\",\n \"forma_pago_p\": \"03\",\n \"moneda_p\": \"MXN\",\n \"monto\": 1160,\n \"num_operacion\": \"OP-0001\"\n }\n ],\n \"rfc_emisor\": \"EKU9003173C9\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-api", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.ipsofactura.com/cfdi/complemento-pago")
.header("x-api", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"pagos\": [\n {\n \"documentos_relacionados\": [\n {\n \"factura_id\": \"<id-del-CFDI-PPD>\",\n \"imp_pagado\": 1160\n }\n ],\n \"fecha_pago\": \"2026-07-21T09:55:00\",\n \"forma_pago_p\": \"03\",\n \"moneda_p\": \"MXN\",\n \"monto\": 1160,\n \"num_operacion\": \"OP-0001\"\n }\n ],\n \"rfc_emisor\": \"EKU9003173C9\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.ipsofactura.com/cfdi/complemento-pago")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-api"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"pagos\": [\n {\n \"documentos_relacionados\": [\n {\n \"factura_id\": \"<id-del-CFDI-PPD>\",\n \"imp_pagado\": 1160\n }\n ],\n \"fecha_pago\": \"2026-07-21T09:55:00\",\n \"forma_pago_p\": \"03\",\n \"moneda_p\": \"MXN\",\n \"monto\": 1160,\n \"num_operacion\": \"OP-0001\"\n }\n ],\n \"rfc_emisor\": \"EKU9003173C9\"\n}"
response = http.request(request)
puts response.read_body{
"documentos_relacionados": [
{
"id_documento": "0bcfae68-...",
"imp_pagado": "1160.00",
"imp_saldo_insoluto": "0.00",
"liquidado": true,
"num_parcialidad": 1
}
],
"fecha_timbrado": "2026-07-21T09:56:00",
"id": "169fe189-...",
"sello_cfdi": "...",
"sello_sat": "...",
"uuid": "b0440bb7-..."
}{
"code": "VALIDATION_ERROR",
"id": "<string>",
"idempotencyKey": "<string>",
"message": "CFDI40147: El RFC del receptor no se encuentra en la lista de contribuyentes inscritos no cancelados del SAT",
"retryAfter": 123,
"sat_code": "CFDI40147"
}{
"code": "VALIDATION_ERROR",
"id": "<string>",
"idempotencyKey": "<string>",
"message": "CFDI40147: El RFC del receptor no se encuentra en la lista de contribuyentes inscritos no cancelados del SAT",
"retryAfter": 123,
"sat_code": "CFDI40147"
}{
"code": "VALIDATION_ERROR",
"id": "<string>",
"idempotencyKey": "<string>",
"message": "CFDI40147: El RFC del receptor no se encuentra en la lista de contribuyentes inscritos no cancelados del SAT",
"retryAfter": 123,
"sat_code": "CFDI40147"
}{
"code": "VALIDATION_ERROR",
"id": "<string>",
"idempotencyKey": "<string>",
"message": "CFDI40147: El RFC del receptor no se encuentra en la lista de contribuyentes inscritos no cancelados del SAT",
"retryAfter": 123,
"sat_code": "CFDI40147"
}{
"code": "VALIDATION_ERROR",
"id": "<string>",
"idempotencyKey": "<string>",
"message": "CFDI40147: El RFC del receptor no se encuentra en la lista de contribuyentes inscritos no cancelados del SAT",
"retryAfter": 123,
"sat_code": "CFDI40147"
}Authorizations
API key emitida por Ipsofactura, enviada en el header x-api.
Headers
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.
100"order-4711"
Body
1Show child attributes
Show child attributes
1\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(Z|[+-]\d{2}:\d{2})?Llave estable opcional (por ejemplo, el id del pago de origen). Reenviar la misma llave devuelve el CFDI ya timbrado en lugar de crear otra parcialidad. Máximo 100 caracteres ASCII imprimibles, sin espacios.
100^[\x21-\x7E]+$"pago-2026-08-0042"
Opcional y sólo útil en el sandbox: RFC reservado que fuerza un escenario (los mismos que en POST /cfdi/timbrar). No se timbra — el receptor del complemento lo determina el CFDI relacionado, como exige el SAT — y en producción se ignora.
13