Skip to content
On this page

Timbrado desde XML ​

Envía un XML de CFDI 4.0 previamente sellado con tus CSD y Timbra se encarga de timbrarlo ante el PAC y el SAT para obtener el Timbre Fiscal Digital.

POST/api/masivo/cfdi33/stamp/json/v4

Timbra un XML de CFDI 4.0 previamente sellado.

Auth: BearerContent-Type: application/jsonConsume timbre: Solo en Live

Request ​

El cuerpo es un objeto JSON con un solo campo:

CampoTipoObligatorioDescripción
datastringSíContenido completo del XML prefirmado/sellado como texto.
json
{
  "data": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><cfdi:Comprobante ...>...</cfdi:Comprobante>"
}

¿Prefieres no generar ni sellar el XML manualmente?

Este endpoint está diseñado para sistemas que ya generan el XML y lo firman localmente con su Certificado de Sello Digital (CSD).

  • Si generas el XML pero no quieres sellarlo localmente, utiliza el endpoint Emisión de XML sin sellar (/cfdi33/issue/v4) para que Timbra lo selle con tu CSD automáticamente.
  • Si prefieres enviar un simple objeto con los datos y que Timbra genere el XML, calcule los impuestos, lo selle con tu CSD y lo timbre automáticamente, utiliza Timbrado desde JSON.

XML de ejemplo (Pre-sellado) ​

El XML enviado debe estar previamente sellado y contener los atributos Sello, NoCertificado y Certificado:

xml
<?xml version="1.0" encoding="UTF-8"?>
<cfdi:Comprobante
  xmlns:cfdi="http://www.sat.gob.mx/cfd/4"
  Version="4.0"
  Serie="F"
  Folio="101"
  Fecha="2026-10-21T12:00:00"
  Sello="iW2Y..."
  NoCertificado="30001000000400002434"
  Certificado="MIIF..."
  FormaPago="01"
  MetodoPago="PUE"
  LugarExpedicion="26015"
  Moneda="MXN"
  TipoDeComprobante="I"
  Exportacion="01"
  SubTotal="1000.00"
  Total="1160.00">
  <cfdi:Emisor Rfc="EKU9003173C9" Nombre="ESCUELA KEMPER URGATE" RegimenFiscal="601"/>
  <cfdi:Receptor
    Rfc="URE180429TM6"
    Nombre="UNIVERSIDAD ROBOTICA ESPAÑOLA"
    DomicilioFiscalReceptor="86991"
    RegimenFiscalReceptor="601"
    UsoCFDI="G01"/>
  <cfdi:Conceptos>
    <cfdi:Concepto
      ClaveProdServ="84111506"
      Cantidad="1"
      ClaveUnidad="E48"
      Descripcion="Servicio de consultoría"
      ValorUnitario="1000.00"
      Importe="1000.00"
      ObjetoImp="02">
      <cfdi:Impuestos>
        <cfdi:Traslados>
          <cfdi:Traslado Base="1000.00" Impuesto="002" TipoFactor="Tasa" TasaOCuota="0.160000" Importe="160.00"/>
        </cfdi:Traslados>
      </cfdi:Impuestos>
    </cfdi:Concepto>
  </cfdi:Conceptos>
  <cfdi:Impuestos TotalImpuestosTrasladados="160.00">
    <cfdi:Traslados>
      <cfdi:Traslado Base="1000.00" Impuesto="002" TipoFactor="Tasa" TasaOCuota="0.160000" Importe="160.00"/>
    </cfdi:Traslados>
  </cfdi:Impuestos>
</cfdi:Comprobante>

Ejemplos ​

bash
# Convierte el XML a un string JSON y envía la petición
jq -Rs '{data: .}' factura.xml > payload.json

curl -X POST "https://dashboard.timbra.com.mx/api/masivo/cfdi33/stamp/json/v4" \
  -H "Authorization: Bearer tmb_test_tu_api_key_aqui" \
  -H "Content-Type: application/json" \
  -d @payload.json
js
import fs from 'node:fs'

const xml = fs.readFileSync('factura.xml', 'utf8')

const response = await fetch(
  'https://dashboard.timbra.com.mx/api/masivo/cfdi33/stamp/json/v4',
  {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.TIMBRA_API_KEY}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ data: xml })
  }
)

const result = await response.json()

if (!response.ok) {
  throw new Error(result.message)
}

console.log('UUID:', result.data.uuid)
console.log('XML timbrado:', result.data.cfdi)
python
import os
import requests

with open("factura.xml", "r", encoding="utf-8") as archivo:
    xml = archivo.read()

response = requests.post(
    "https://dashboard.timbra.com.mx/api/masivo/cfdi33/stamp/json/v4",
    headers={
        "Authorization": f"Bearer {os.environ['TIMBRA_API_KEY']}",
        "Content-Type": "application/json",
    },
    json={"data": xml},
)

result = response.json()

if response.status_code != 200:
    raise Exception(result["message"])

print("UUID:", result["data"]["uuid"])
print("XML timbrado:", result["data"]["cfdi"])

Response ​

json
{
  "status": "success",
  "data": {
    "msg": "Se ha timbrado correctamente",
    "uuid": "A1B2C3D4-5E6F-7890-ABCD-EF1234567890",
    "xml": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><cfdi:Comprobante ...>",
    "cfdi": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><cfdi:Comprobante ...>"
  }
}

data.cfdi contiene el XML timbrado. Por compatibilidad, la respuesta también incluye data.xml con el mismo contenido.


Emisión con XML sin sellar (/cfdi33/issue/v4) ​

Si tu sistema ya construye la estructura del XML de CFDI 4.0 pero no deseas encargarte del sellado criptográfico con los certificados .cer y .key, envía el XML directamente a /cfdi33/issue/v4. Timbra aplicará el CSD y timbrará ante el SAT en una sola operación.

POST/api/masivo/cfdi33/issue/v4

Emite y timbra un XML sin sellar (sellado automático con CSD)

Auth: BearerContent-Type: multipart/form-data o application/jsonConsume timbre: Solo en Live

Formatos aceptados ​

Puedes enviar el XML sin sellar de dos formas equivalentes:

  1. multipart/form-data: Como archivo adjunto o campo con nombre xml.
  2. application/json: Con el objeto { "data": "<cfdi:Comprobante ...>" }.
bash
curl -X POST "https://dashboard.timbra.com.mx/api/masivo/cfdi33/issue/v4" \
  -H "Authorization: Bearer tmb_test_tu_api_key_aqui" \
  -F "xml=@factura_sin_sellar.xml"
bash
jq -Rs '{data: .}' factura_sin_sellar.xml > payload.json

curl -X POST "https://dashboard.timbra.com.mx/api/masivo/cfdi33/issue/v4" \
  -H "Authorization: Bearer tmb_test_tu_api_key_aqui" \
  -H "Content-Type: application/json" \
  -d @payload.json
js
import fs from 'node:fs'

const xml = fs.readFileSync('factura_sin_sellar.xml', 'utf8')

const response = await fetch(
  'https://dashboard.timbra.com.mx/api/masivo/cfdi33/issue/v4',
  {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.TIMBRA_API_KEY}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ data: xml })
  }
)

const result = await response.json()
console.log('UUID:', result.data.uuid)
python
import os
import requests

with open("factura_sin_sellar.xml", "rb") as f:
    files = {"xml": ("factura.xml", f, "application/xml")}
    response = requests.post(
        "https://dashboard.timbra.com.mx/api/masivo/cfdi33/issue/v4",
        headers={"Authorization": f"Bearer {os.environ['TIMBRA_API_KEY']}"},
        files=files,
    )

result = response.json()
print("UUID:", result["data"]["uuid"])

Errores ​

HTTPCausa
400Falta el campo data o el XML no es válido.
400El RFC del emisor no es un RFC de prueba válido en Sandbox.
400El PAC rechazó el XML. El detalle viene en message.
400No hay saldo ni suscripción activa en Live.
401La API Key falta, es inválida o fue revocada.
500Error interno al procesar el timbrado.

Ejemplo cuando falta el XML:

json
{
  "status": "error",
  "message": "El cuerpo de la petición debe contener el campo \"data\" con el XML a timbrar."
}

Notas ​

  • En Live, cada CFDI timbrado descuenta un timbre. En Test no se descuenta nada.
  • Si el XML ya fue timbrado, la API puede devolver el UUID existente con el mensaje El XML ya ha sido timbrado previamente.
  • El CFDI timbrado se guarda en tu cuenta solo en Live.
  • El XML puede ir sin Sello, Certificado y NoCertificado; Timbra los genera automáticamente antes de timbrar.