Test XML generator
Generate a CFDI 4.0 issued by a SAT test RFC and sealed with its test CSD, ready for invoice/stampxml or invoice/stampxmlfile. Use it to test your integration without your production certificate.
Security check in progress.
The generated XML will appear here
- A CFDI 4.0 issued by a SAT test RFC with its test CSD.
- The original string and the seal, computed with the SAT's official XSLT.
- The commands to send it to the sandbox with stampxml or stampxmlfile.
- The issuer's .cer and .key, for sealing with your own system.
Use cases
1. Test stampxml without your production CSD
Keep the “Sealed with the test CSD” option, generate the XML and download it. Send it to the sandbox with your sandbox account API key, as described in Sending to stampxml or stampxmlfile. This verifies that your code encodes the Base64 or builds the multipart request correctly, parses the response and stores the stamped XML from cfdi.XmlBase64 before you install your certificate on a server. If you are migrating from a PAC, the guide Migrating from a PAC? Use stampxml describes the full process.
2. Test your system's sealing
Select “Unsealed”. The XML is generated with the test issuer's NoCertificado and Certificado and an empty Sello. Its .cer and .key are available in the “Test CSD” tab. Your system must sign the original string with that key and assign the result to the Sello attribute. Then paste the XML into the XML validator, which recomputes the string with the SAT XSLT and reports whether the seal matches. If it does not, compare your system's string with the one the validator shows.
# 1. Convert the test CSD key to PEM (password 12345678a)
openssl pkcs8 -inform DER -in CSD.key -passin pass:12345678a -out llave.pem
# 2. Sign the original string with SHA-256 (cadena.txt without a trailing newline)
openssl dgst -sha256 -sign llave.pem -out sello.bin cadena.txt
# 3. Get the seal in Base64, on a single line, for the Sello attribute
openssl base64 -A -in sello.bin
# The Certificado attribute is the .cer in Base64, also on a single line
openssl base64 -A -in CSD.cer3. Test rejection cases
The “Rejection cases” group in the issuer selector includes the test CSDs that are not in the LCO and the test RFCs with EFOS status. The XML is sealed correctly, but stamping is rejected. Use them to see the API response and to verify that your system shows the error instead of retrying indefinitely. Each code is described in the error matrix.
4. Sample XML by CFDI type
The generator covers income invoices with 16%, 8% or 0% VAT, exempt invoices, with or without withholdings; credit notes with a related CFDI; transfers with value 0; payments 2.0 complements for PPD invoices and global invoices to the general public. These XMLs work as test cases for your XML reader or PDF generator.
Sending to stampxml or stampxmlfile
Both methods stamp the same XML and return the same response. Only the request format differs: stampxml takes the XML in Base64 in the xmlbase64 field of a form, and stampxmlfile takes it as a file in the xml field of a multipart request. The full reference is in Stamp sealed XML.
/invoice/stampxml/invoice/stampxmlfile# Base64 in the form (stampxml)
curl -sS -X POST "https://sandbox-app.facturadigital.com.mx/api/v5/invoice/stampxml" \
-H "X-Api-Key: $FD_API_KEY" \
--data-urlencode "xmlbase64=$(base64 < cfdi-prueba.xml | tr -d '\n')"
# The file as is (stampxmlfile)
curl -sS -X POST "https://sandbox-app.facturadigital.com.mx/api/v5/invoice/stampxmlfile" \
-H "X-Api-Key: $FD_API_KEY" \
-F "xml=@cfdi-prueba.xml"<?php
$xml = file_get_contents('cfdi-prueba.xml');
$ch = curl_init('https://sandbox-app.facturadigital.com.mx/api/v5/invoice/stampxml');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ['X-Api-Key: ' . getenv('FD_API_KEY')],
CURLOPT_POSTFIELDS => http_build_query(['xmlbase64' => base64_encode($xml)]),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 120,
]);
$r = json_decode(curl_exec($ch), true);
echo $r['code'], ' ', $r['message'], PHP_EOL;
// stampxmlfile: the file as multipart
// CURLOPT_POSTFIELDS => ['xml' => new CURLFile('cfdi-prueba.xml', 'text/xml')]import { readFile } from "node:fs/promises";
const xml = await readFile("cfdi-prueba.xml");
const res = await fetch("https://sandbox-app.facturadigital.com.mx/api/v5/invoice/stampxml", {
method: "POST",
headers: { "X-Api-Key": process.env.FD_API_KEY },
body: new URLSearchParams({ xmlbase64: xml.toString("base64") }),
signal: AbortSignal.timeout(120_000),
});
const r = await res.json();
console.log(r.code, r.message, r.cfdi?.UUID);
// stampxmlfile: the file as multipart
// const form = new FormData();
// form.append("xml", new Blob([xml], { type: "text/xml" }), "cfdi-prueba.xml");import base64, os, requests
with open("cfdi-prueba.xml", "rb") as f:
xml = f.read()
r = requests.post(
"https://sandbox-app.facturadigital.com.mx/api/v5/invoice/stampxml",
headers={"X-Api-Key": os.environ["FD_API_KEY"]},
data={"xmlbase64": base64.b64encode(xml).decode()},
timeout=120,
)
print(r.json())
# stampxmlfile: the file as multipart
# requests.post(".../invoice/stampxmlfile", headers=..., files={"xml": open("cfdi-prueba.xml", "rb")})var xml = await File.ReadAllBytesAsync("cfdi-prueba.xml");
using var http = new HttpClient { Timeout = TimeSpan.FromSeconds(120) };
http.DefaultRequestHeaders.Add("X-Api-Key", Environment.GetEnvironmentVariable("FD_API_KEY"));
var body = new FormUrlEncodedContent(new Dictionary<string, string> {
["xmlbase64"] = Convert.ToBase64String(xml),
});
var res = await http.PostAsync("https://sandbox-app.facturadigital.com.mx/api/v5/invoice/stampxml", body);
Console.WriteLine(await res.Content.ReadAsStringAsync());
// stampxmlfile: the file as multipart
// var form = new MultipartFormDataContent { { new ByteArrayContent(xml), "xml", "cfdi-prueba.xml" } };On success, the response is HTTP 200 and the stamped XML, with the TimbreFiscalDigital, is returned in cfdi.XmlBase64.
{
"message": "XML stamped successfully",
"code": 200,
"cfdi": {
"UUID": "6F0C2E1A-9B3D-4C7E-8A51-2D4F6B8C0E13",
"FechaTimbrado": "2026-11-04T10:15:30",
"NoCertificado": "30001000000500003416",
"SelloCFD": "Q2V4...Zm9vYmFy",
"SelloSAT": "Tm9w...ZGF0YQ==",
"XmlBase64": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4..."
}
}- If you send the same XML twice, the second request returns
timbre_previowith the original UUID and does not use another folio. For a new test, generate another XML. - The SAT does not allow stamping a CFDI issued more than 72 hours earlier. The generator sets
Fechato the local time at the issuer's postal code minus 5 minutes, so the XML must be stamped within that period. - On error, the response is HTTP 400 and
messagebegins with the SAT error code. Look up the code in the error matrix.
Test RFCs and CSDs
These are the SAT test RFCs for the test environment, each with its CSD. The password for every key is 12345678a. When an RFC is used as the recipient, the name and postal code must match this list, because the test environment compares them with the SAT RFC list (CFDI40145 and CFDI40147).
They stamp in the sandbox and can be used as issuer or recipient.
| RFC | Name | Postal code | Tax regime | NoCertificado |
|---|---|---|---|---|
| ESCUELA KEMPER URGATE | 601 | |||
| XENON INDUSTRIAL ARTICLES | 622 | |||
| HERRERIA & ELECTRICOS | 601 | |||
| INDISTRIA ILUMINADORA DE ALMACENES | 626 | |||
| INNOVACION VALOR Y DESARROLLO | 601 | |||
| INTERNACIONAL XIMBO Y SABORES | 603 | |||
| JIMENEZ ESTRADA SALAS | 622 | |||
| KERNEL INDUSTIA JUGUETERA | 622 | |||
| LUCES & OBRAS | 626 | |||
| ORGANICOS ÑAVEZ OSORIO | 601 | |||
| S & SOFTWARE | 624 | |||
| UNIVERSIDAD ROBOTICA ESPAÑOLA | 601 | |||
| ZAPATERIA URTADO ÑERI | 626 |
The tax regime matches the tax characteristics registered for each test RFC. The SAT does not compare it with its registry and only checks that it matches the type of taxpayer.
XML contents
- CFDI 4.0 in MXN with
Exportacion="01". The series is FD, NC, TR or RP depending on the CFDI type, with a random folio. - Amounts with two decimals. Each item tax is rounded to cents and totals are the sum of the items, as required by the SAT validation matrix.
- With 8% VAT,
LugarExpedicionis postal code 88000 (Nuevo Laredo, Tamaulipas). Stamping only accepts that rate when the place of issue has the border-region incentive in the SAT postal code catalog. - Withholdings only when they apply, that is, when an individual under regime 606, 612 or 626 invoices a company. ISR is withheld at 10% (1.25% under RESICO), plus two thirds of the VAT.
- For the general public, the RFC is XAXX010101000 with regime 616, use S01 and the issuer's postal code. Income invoices include
InformacionGlobalwith a monthly period. - The payment CFDI has
Moneda="XXX", one “Pago” item with value 0 and thepago20:Pagoscomplement, with a payment that settles the PPD invoice in a single installment, along with its taxes and totals. - The original string is generated with the SAT XSLT and the seal uses RSA with SHA-256. Every XML is validated against the SAT schema.
The generator does not produce payroll, Carta Porte, foreign trade or CFDIs in other currencies. For those cases, generate the XML in your system, seal it with the test CSD downloaded here and check it in the XML validator, which supports every SAT complement.
Checked against the production API v5 on 2026-11-03.