Validaciones SAT
Una validación SAT verifica un CFDI timbrado contra los servicios oficiales del SAT: la estructura del XML, la vigencia del certificado del emisor, el sello del comprobante y el del timbre, el estatus del comprobante y las listas negras de los artículos 69-B y 69-B Bis del CFF. Cada verificación es un tipo de validación que se solicita de forma independiente.
Cada tipo solicitado consume un crédito de validación. El cobro es todo o nada: si el saldo no alcanza
para todos los tipos solicitados, no se ejecuta ninguno y la API responde 403. Los créditos de validación
son independientes de los timbres: se consultan en availableValidationBalance de cada persona
y se transfieren por el endpoint de timbres con creditType: 2.
Modelo tipo de validación
El modelo tipo de validación (SatValidationType) describe una de las verificaciones que Fiscalapi puede
ejecutar sobre un CFDI. Es un catálogo de solo lectura de siete tipos.
Propiedades
- Name
id- Type
- string?
- Description
Identificador del tipo de validación.
- Type
- enum:
- Values
- "sat.xml.structure""sat.certificate.validity""sat.cfdi.sello"
- Name
description- Type
- string?
- Description
Descripción de lo que verifica el tipo de validación.
Modelo resultado
El modelo resultado (SatValidationResult) es lo que devuelve la ejecución de una validación: un elemento por
cada tipo solicitado, siempre en el orden del catálogo.
Propiedades
- Name
type- Type
- object (SatValidationType)?
Tipo de validación que se ejecutó.
- Name
id- Type
- string?
- Description
Identificador del tipo de validación.
- Name
description- Type
- string?
- Description
Descripción de lo que verifica el tipo de validación.
- Name
status- Type
- object (SatValidationStatus)?
Estatus obtenido al ejecutar la validación.
- Name
id- Type
- string?
- Description
Identificador del estatus. Cada tipo de validación admite solo un subconjunto de estos valores: el que devuelve listar estatus de un tipo.
- Type
- enum:
- Values
- "Valido""Vigente""NoListado"
- Name
description- Type
- string?
- Description
Descripción del estatus.
- Name
details- Type
- string?
- Description
Datos concretos del caso en texto libre, por ejemplo el número de certificado o la fecha de corte del listado consultado. Puede ser
null. Es informativo: no debe interpretarse programáticamente.
- Name
passed- Type
- boolean?
- Description
truesi el estatus obtenido es aprobatorio para ese tipo de validación.
passed es el veredicto binario del tipo de validación; status.id identifica el estatus concreto que lo
produjo y es el valor que conviene registrar.
Listar tipos de validación
Este endpoint devuelve todos los tipos de validación activos, en el orden del catálogo. No recibe parámetros y no consume créditos.
Request
curl --location 'https://test.fiscalapi.com/api/v4/sat-validations' \
--header 'X-TENANT-KEY: <tenant_key>' \
--header 'X-TIME-ZONE: America/Mexico_City' \
--header 'X-API-KEY: <api_key>'
Response
{
"data": [
{
"id": "sat.xml.structure",
"description": "Verifica mediante el servicio oficial de verificación de CFDI del SAT que el XML sea un documento bien formado y cumpla la estructura del Anexo 20 de su versión y la de los complementos declarados en schemaLocation. Detecta atributos obligatorios ausentes, valores fuera de catálogo y patrones inválidos."
},
{
"id": "sat.certificate.validity",
"description": "Comprueba que la fecha de emisión del CFDI esté comprendida entre NotBefore y NotAfter del certificado del emisor contenido en Certificado. Un CFDI firmado fuera de la vigencia del certificado carece de validez aunque el sello verifique."
},
{
"id": "sat.cfdi.sello",
"description": "Verifica mediante el servicio oficial de verificación de CFDI del SAT que el atributo Sello corresponda a la cadena original del comprobante y a la llave pública del certificado contenido en Certificado. Confirma que el contenido no fue alterado después de firmarse y que el firmante posee la llave privada de ese certificado."
},
{
"id": "sat.tfd.sello",
"description": "Verifica mediante el servicio oficial de verificación de CFDI del SAT que el atributo SelloSAT del TimbreFiscalDigital corresponda al certificado del SAT identificado por NoCertificadoSAT. Confirma que el timbre fue emitido por el SAT a través de un PAC y no fue fabricado ni modificado."
},
{
"id": "sat.cfdi.status",
"description": "Consulta ConsultaCFDIService del SAT con UUID, RFC emisor, RFC receptor y total, y reporta el estado actual del comprobante. Es la única validación que refleja cancelaciones posteriores a la emisión."
},
{
"id": "sat.blacklist.69b",
"description": "Busca el RFC del emisor en el listado completo del artículo 69-B CFF y devuelve la situación vigente. Definitivo implica que los CFDI del emisor no producen efectos fiscales y activa el plazo de 30 días hábiles para el receptor."
},
{
"id": "sat.blacklist.69bbis",
"description": "Busca el RFC del emisor en el listado del artículo 69-B Bis CFF (transmisión indebida del derecho a disminuir pérdidas fiscales) y devuelve la situación vigente. Relevante en operaciones con partes relacionadas y reestructuras."
}
],
"succeeded": true,
"message": "",
"details": "",
"httpStatusCode": 200
}
Obtener tipo de validación por ID
Este endpoint te permite obtener un tipo de validación por su ID. El ID contiene puntos y se envía tal
cual, sin codificar. Devuelve 404 si el tipo no existe o está inactivo. No consume créditos.
Request
curl --location 'https://test.fiscalapi.com/api/v4/sat-validations/sat.xml.structure' \
--header 'X-TENANT-KEY: <tenant_key>' \
--header 'X-TIME-ZONE: America/Mexico_City' \
--header 'X-API-KEY: <api_key>'
Response
{
"data": {
"id": "sat.xml.structure",
"description": "Verifica mediante el servicio oficial de verificación de CFDI del SAT que el XML sea un documento bien formado y cumpla la estructura del Anexo 20 de su versión y la de los complementos declarados en schemaLocation. Detecta atributos obligatorios ausentes, valores fuera de catálogo y patrones inválidos."
},
"succeeded": true,
"message": "",
"details": "",
"httpStatusCode": 200
}
Listar estatus de un tipo
Este endpoint devuelve los estatus que un tipo de validación puede tomar al ejecutarse: primero los
aprobatorios y, dentro de cada grupo, ordenados alfabéticamente por id. Es de solo lectura: no ejecuta
la validación ni consume créditos. La respuesta no
incluye el campo passed; esa correspondencia está en modelo resultado. Devuelve
404 si el tipo no existe o está inactivo.
Request
curl --location 'https://test.fiscalapi.com/api/v4/sat-validations/sat.xml.structure/statuses' \
--header 'X-TENANT-KEY: <tenant_key>' \
--header 'X-TIME-ZONE: America/Mexico_City' \
--header 'X-API-KEY: <api_key>'
Response
{
"data": [
{
"id": "Valido",
"description": "Documento bien formado que cumple el XSD del Anexo 20 de su versión y los XSD de los complementos declarados en schemaLocation, según el verificador del SAT."
},
{
"id": "Invalido",
"description": "El documento no está bien formado o el verificador del SAT reporta que incumple el XSD del Anexo 20 o el de alguno de sus complementos."
},
{
"id": "NoDisponible",
"description": "El verificador del SAT no respondió o rechazó la consulta; la estructura no pudo determinarse y debe reintentarse."
},
{
"id": "Omitido",
"description": "No se evaluó porque el CFDI no contiene el complemento TimbreFiscalDigital; el verificador del SAT solo evalúa comprobantes timbrados."
}
],
"succeeded": true,
"message": "",
"details": "",
"httpStatusCode": 200
}
Validar
Este endpoint ejecuta las validaciones solicitadas y devuelve un resultado por cada tipo. Los resultados vuelven en el orden del catálogo, no en el orden en que los solicitaste.
Modelo
- Name
xml- Type
- string?
- Description
CFDI timbrado completo, codificado en Base64. Excluyente con
tin. Conxmlpuedes solicitar cualquier tipo de validación. Las listas negras consultan el RFC del nodocfdi:Emisor. Máximo 4,000,000 de caracteres; el tamaño máximo del cuerpo de la petición es de 10 MB.
- Name
tin- Type
- string?
- Description
RFC a consultar. Excluyente con
xml. Continsolo puedes solicitar listas negras (sat.blacklist.69bysat.blacklist.69bbis). El RFC no se contrasta contra ningún CFDI: la respuesta refleja únicamente la situación del RFC enviado.
- Name
validationTypes- Type
- array of strings
- required
- Description
Tipos de validación a ejecutar. No admite elementos vacíos ni duplicados, y todos deben existir y estar activos.
- Type
- enum:
- Values
- "sat.xml.structure""sat.certificate.validity""sat.cfdi.sello"
En el ambiente de pruebas los resultados son ficticios y aleatorios. En
https://test.fiscalapi.com la API no consulta al SAT ni a su verificador, y ni siquiera evalúa el XML
que envías: devuelve un estatus elegido al azar de entre los que ese tipo admite. Por eso dos llamadas
idénticas dan resultados distintos, details siempre viene null y nunca verás NoDisponible ni
Omitido, ni aun enviando un XML inválido o sin timbrar. Los créditos se cobran igual. Este ambiente
sirve únicamente para conocer el contrato y probar tu integración: nunca lo uses como fuente de
verdad fiscal ni tomes una decisión de negocio con su resultado. Para eso, producción. Los details que
ves en los ejemplos de esta página provienen de producción.
Debes enviar xml o tin, nunca ambos y nunca ninguno. Cada tipo solicitado consume un crédito de
validación, incluso cuando el resultado es NoDisponible u Omitido.
Un resultado adverso no es un error: si la validación se ejecutó, la respuesta es 200 con
passed: false. Una petición mal formada devuelve 400 y no consume créditos; un saldo insuficiente
devuelve 403 con Créditos de validación insuficientes: se requieren N y el saldo es M y tampoco
consume créditos, porque el cobro ocurre después de validar la petición. Si la ejecución falla por un
problema técnico (500), el débito se reembolsa automáticamente como un movimiento de tipo reverso en el
ledger de timbres. En cambio, si cancelas la petición o se cierra la conexión, el consumo se
conserva y no recibes resultados: para entonces las consultas al SAT ya se pagaron.
Que el xml no sea un CFDI timbrado tampoco es un 400: la respuesta es 200, se cobran todos los
créditos solicitados y cada tipo reporta lo que pudo determinar. Un XML mal formado, o cuya raíz no es
cfdi:Comprobante, devuelve Invalido en sat.xml.structure (con el error del lector en details) y
Omitido en los demás tipos. Un CFDI legible pero sin timbrar devuelve Omitido en
sat.xml.structure, sat.cfdi.sello, sat.tfd.sello y sat.cfdi.status, mientras que
sat.certificate.validity y las listas negras sí se evalúan.
Consulta errores y modelo de respuesta.
Request
curl --location 'https://test.fiscalapi.com/api/v4/sat-validations' \
--header 'X-TENANT-KEY: <tenant_key>' \
--header 'X-TIME-ZONE: America/Mexico_City' \
--header 'Content-Type: application/json' \
--header 'X-API-KEY: <api_key>' \
--data-raw '{
"xml": "<xml-timbrado-en-base64>",
"validationTypes": [
"sat.xml.structure",
"sat.certificate.validity",
"sat.cfdi.sello",
"sat.tfd.sello",
"sat.cfdi.status",
"sat.blacklist.69b",
"sat.blacklist.69bbis"
]
}'
Response
{
"data": [
{
"type": {
"id": "sat.xml.structure",
"description": "Verifica mediante el servicio oficial de verificación de CFDI del SAT que el XML sea un documento bien formado y cumpla la estructura del Anexo 20 de su versión y la de los complementos declarados en schemaLocation. Detecta atributos obligatorios ausentes, valores fuera de catálogo y patrones inválidos."
},
"status": {
"id": "Valido",
"description": "Documento bien formado que cumple el XSD del Anexo 20 de su versión y los XSD de los complementos declarados en schemaLocation, según el verificador del SAT.",
"details": null
},
"passed": true
},
{
"type": {
"id": "sat.certificate.validity",
"description": "Comprueba que la fecha de emisión del CFDI esté comprendida entre NotBefore y NotAfter del certificado del emisor contenido en Certificado. Un CFDI firmado fuera de la vigencia del certificado carece de validez aunque el sello verifique."
},
"status": {
"id": "Vigente",
"description": "La fecha de emisión del CFDI está comprendida entre NotBefore y NotAfter del certificado del emisor.",
"details": "Emisión 2026-08-18T11:56:42 (hora de México); certificado 00001000000720012093 de MAX0611157H8 vigente del 2025-11-04 al 2029-11-04"
},
"passed": true
},
{
"type": {
"id": "sat.cfdi.sello",
"description": "Verifica mediante el servicio oficial de verificación de CFDI del SAT que el atributo Sello corresponda a la cadena original del comprobante y a la llave pública del certificado contenido en Certificado. Confirma que el contenido no fue alterado después de firmarse y que el firmante posee la llave privada de ese certificado."
},
"status": {
"id": "Valido",
"description": "El verificador del SAT confirma que el atributo Sello verifica contra la cadena original del comprobante con la llave pública del certificado del emisor.",
"details": null
},
"passed": true
},
{
"type": {
"id": "sat.tfd.sello",
"description": "Verifica mediante el servicio oficial de verificación de CFDI del SAT que el atributo SelloSAT del TimbreFiscalDigital corresponda al certificado del SAT identificado por NoCertificadoSAT. Confirma que el timbre fue emitido por el SAT a través de un PAC y no fue fabricado ni modificado."
},
"status": {
"id": "Valido",
"description": "El verificador del SAT confirma que el atributo SelloSAT verifica contra la cadena original del TimbreFiscalDigital con el certificado del SAT identificado por NoCertificadoSAT.",
"details": null
},
"passed": true
},
{
"type": {
"id": "sat.cfdi.status",
"description": "Consulta ConsultaCFDIService del SAT con UUID, RFC emisor, RFC receptor y total, y reporta el estado actual del comprobante. Es la única validación que refleja cancelaciones posteriores a la emisión."
},
"status": {
"id": "Vigente",
"description": "ConsultaCFDIService reporta el comprobante como Vigente; se incluyen EsCancelable y EstatusCancelacion devueltos por el SAT.",
"details": "Estado Vigente; CodigoEstatus S - Comprobante obtenido satisfactoriamente.; EsCancelable Cancelable con aceptación; EstatusCancelacion "
},
"passed": true
},
{
"type": {
"id": "sat.blacklist.69b",
"description": "Busca el RFC del emisor en el listado completo del artículo 69-B CFF y devuelve la situación vigente. Definitivo implica que los CFDI del emisor no producen efectos fiscales y activa el plazo de 30 días hábiles para el receptor."
},
"status": {
"id": "NoListado",
"description": "El RFC del emisor no figura en ninguna etapa del listado completo del artículo 69-B al corte indicado.",
"details": "RFC MAX0611157H8; corte del listado 2026-07-31"
},
"passed": true
},
{
"type": {
"id": "sat.blacklist.69bbis",
"description": "Busca el RFC del emisor en el listado del artículo 69-B Bis CFF (transmisión indebida del derecho a disminuir pérdidas fiscales) y devuelve la situación vigente. Relevante en operaciones con partes relacionadas y reestructuras."
},
"status": {
"id": "NoListado",
"description": "El RFC del emisor no figura en ninguna etapa del listado del artículo 69-B Bis al corte indicado.",
"details": "RFC MAX0611157H8; corte del listado 2026-06-05"
},
"passed": true
}
],
"succeeded": true,
"message": "",
"details": "",
"httpStatusCode": 200
}
Validar solo por RFC
Cuando solo necesitas saber si un RFC está en las listas negras del SAT, envía tin en lugar de xml.
Es el mismo endpoint y el mismo modelo, pero en esta modalidad únicamente se admiten sat.blacklist.69b y
sat.blacklist.69bbis: solicitar cualquier otro tipo devuelve 400. Consume un crédito por cada tipo
solicitado, igual que la validación con xml.
Rige la misma advertencia que en validar: en el ambiente de pruebas el resultado es ficticio y aleatorio, y no debe usarse para decidir nada.
Request
curl --location 'https://test.fiscalapi.com/api/v4/sat-validations' \
--header 'X-TENANT-KEY: <tenant_key>' \
--header 'X-TIME-ZONE: America/Mexico_City' \
--header 'Content-Type: application/json' \
--header 'X-API-KEY: <api_key>' \
--data-raw '{
"tin": "XAXX010101000",
"validationTypes": [
"sat.blacklist.69b",
"sat.blacklist.69bbis"
]
}'
Response
{
"data": [
{
"type": {
"id": "sat.blacklist.69b",
"description": "Busca el RFC del emisor en el listado completo del artículo 69-B CFF y devuelve la situación vigente. Definitivo implica que los CFDI del emisor no producen efectos fiscales y activa el plazo de 30 días hábiles para el receptor."
},
"status": {
"id": "NoListado",
"description": "El RFC del emisor no figura en ninguna etapa del listado completo del artículo 69-B al corte indicado.",
"details": "RFC XAXX010101000; corte del listado 2026-07-31"
},
"passed": true
},
{
"type": {
"id": "sat.blacklist.69bbis",
"description": "Busca el RFC del emisor en el listado del artículo 69-B Bis CFF (transmisión indebida del derecho a disminuir pérdidas fiscales) y devuelve la situación vigente. Relevante en operaciones con partes relacionadas y reestructuras."
},
"status": {
"id": "NoListado",
"description": "El RFC del emisor no figura en ninguna etapa del listado del artículo 69-B Bis al corte indicado.",
"details": "RFC XAXX010101000; corte del listado 2026-06-05"
},
"passed": true
}
],
"succeeded": true,
"message": "",
"details": "",
"httpStatusCode": 200
}
Transferir créditos de validación
Los créditos de validación se mueven por el endpoint de timbres, con el campo creditType en
2. Con 1 (el valor por omisión) se transfieren timbres. Los saldos nunca se mezclan.
Modelo
- Name
fromPersonId- Type
- string
- required
- Description
ID de la persona origen que envía los créditos. Debe tener saldo suficiente de validaciones.
- Name
toPersonId- Type
- string
- required
- Description
ID de la persona destino que recibe los créditos.
- Name
amount- Type
- integer | number
- required
- Description
Cantidad de créditos a transferir. Debe ser mayor que cero.
- Name
comments- Type
- string?
- Description
Comentarios opcionales sobre la transferencia. Máximo 100 caracteres.
- Name
creditType- Type
- integer | number?
- expandible
- Description
Tipo de crédito a transferir.
- Type
- enum:
- Values
- 12
Default:1
El saldo resultante se consulta en availableValidationBalance del recurso personas,
independiente de availableBalance (timbres).
Request
curl --location 'https://test.fiscalapi.com/api/v4/stamps' \
--header 'X-TENANT-KEY: <tenant_key>' \
--header 'X-TIME-ZONE: America/Mexico_City' \
--header 'Content-Type: application/json' \
--header 'X-API-KEY: <api_key>' \
--data-raw '{
"fromPersonId": "1",
"toPersonId": "bef56254-0892-4558-95c3-f9c8729e4b0e",
"amount": 10,
"comments": "Créditos para validar CFDI",
"creditType": 2
}'
Response
{
"data": true,
"succeeded": true,
"message": "",
"details": "",
"httpStatusCode": 200
}