Resumen

La huella es el SHA-256 de una cadena Campo1=valor1&Campo2=valor2&… codificada en UTF-8, con los campos en un orden fijo y la huella del registro anterior dentro. El resultado va en hexadecimal y en mayúsculas (64 caracteres). La especificación vigente es la versión 0.1.2, de 27 de agosto de 2024.

Campos y orden

Composición de la cadena (especificación de la huella, v0.1.2)
#Registro de altaRegistro de anulación
1IDEmisorFacturaIDEmisorFacturaAnulada
2NumSerieFacturaNumSerieFacturaAnulada
3FechaExpedicionFacturaFechaExpedicionFacturaAnulada
4TipoFacturaHuella (del registro anterior)
5CuotaTotalFechaHoraHusoGenRegistro
6ImporteTotal
7Huella (del registro anterior)
8FechaHoraHusoGenRegistro

Los detalles que hacen fallar el cálculo

  • Nombres de campo literales. Se escriben tal como en el XML, respetando mayúsculas: IDEmisorFactura, no IdEmisorFactura.
  • Primer registro. La huella anterior no existe: el campo queda vacío, …&Huella=&FechaHoraHusoGenRegistro=…. En el XML ese registro lleva PrimerRegistro = «S», pero su propia huella sí se calcula.
  • Espacios. Se eliminan al inicio y al final de cada valor antes de concatenar. La AEAT también los elimina al recibir el XML.
  • Fechas. FechaExpedicionFactura va como DD-MM-AAAA (01-01-2024). FechaHoraHusoGenRegistro va en ISO 8601 con huso (2024-01-01T19:20:30+01:00) y debe ser idéntica a la del XML.
  • Importes. La especificación trata igual los valores con uno o dos decimales («123.1» y «123.10»). Lo seguro es usar exactamente el valor que escribes en el XML. El XSD no admite más de dos decimales.
  • Codificación. UTF-8 (importa si el número de serie lleva caracteres no ASCII).
  • Salida. Hexadecimal en mayúsculas. Muchas librerías devuelven minúsculas por defecto.

Los tres ejemplos oficiales

La especificación incluye tres ejemplos encadenados. Son los vectores de prueba de cualquier implementación:

RegistroDatos que cambianHuella resultante
1. Alta (primer registro)NumSerieFactura 12345678/G33, Huella vacía, 19:20:303C464DAF61ACB827C65FDA19F352A4E3BDC2C640E9E9FC4CC058073F38F12F60
2. AltaNumSerieFactura 12345679/G34, Huella = la del 1, 19:20:35F7B94CFD8924EDFF273501B01EE5153E4CE8F259766F88CF6ACB8935802A2B97
3. Anulación del 2Huella = la del 2, 19:20:40177547C0D57AC74748561D054A9CEC14B4C4EA23D1BEFD6F2E69E3A388F90C68

Datos comunes: IDEmisorFactura 89890001K, FechaExpedicionFactura 01-01-2024, TipoFactura F1, CuotaTotal 12.35, ImporteTotal 123.45 y huso +01:00. La cadena completa del primero es:

IDEmisorFactura=89890001K&NumSerieFactura=12345678/G33&FechaExpedicionFactura=01-01-2024&TipoFactura=F1&CuotaTotal=12.35&ImporteTotal=123.45&Huella=&FechaHoraHusoGenRegistro=2024-01-01T19:20:30+01:00

Código en Python

Solo usa la biblioteca estándar. Al ejecutarlo imprime las tres huellas oficiales:

import hashlib


def huella_alta(emisor, num_serie, fecha, tipo, cuota, importe, huella_anterior, fecha_hora):
    campos = [
        ("IDEmisorFactura", emisor),
        ("NumSerieFactura", num_serie),
        ("FechaExpedicionFactura", fecha),
        ("TipoFactura", tipo),
        ("CuotaTotal", cuota),
        ("ImporteTotal", importe),
        ("Huella", huella_anterior),  # "" en el primer registro
        ("FechaHoraHusoGenRegistro", fecha_hora),
    ]
    cadena = "&".join(f"{k}={v.strip()}" for k, v in campos)
    return hashlib.sha256(cadena.encode("utf-8")).hexdigest().upper()


def huella_anulacion(emisor, num_serie, fecha, huella_anterior, fecha_hora):
    campos = [
        ("IDEmisorFacturaAnulada", emisor),
        ("NumSerieFacturaAnulada", num_serie),
        ("FechaExpedicionFacturaAnulada", fecha),
        ("Huella", huella_anterior),
        ("FechaHoraHusoGenRegistro", fecha_hora),
    ]
    cadena = "&".join(f"{k}={v.strip()}" for k, v in campos)
    return hashlib.sha256(cadena.encode("utf-8")).hexdigest().upper()


if __name__ == "__main__":
    # Ejemplos oficiales de la especificación de la AEAT (v0.1.2)
    h1 = huella_alta("89890001K", "12345678/G33", "01-01-2024", "F1", "12.35", "123.45", "", "2024-01-01T19:20:30+01:00")
    h2 = huella_alta("89890001K", "12345679/G34", "01-01-2024", "F1", "12.35", "123.45", h1, "2024-01-01T19:20:35+01:00")
    h3 = huella_anulacion("89890001K", "12345679/G34", "01-01-2024", h2, "2024-01-01T19:20:40+01:00")
    print(h1)  # 3C464DAF61ACB827C65FDA19F352A4E3BDC2C640E9E9FC4CC058073F38F12F60
    print(h2)  # F7B94CFD8924EDFF273501B01EE5153E4CE8F259766F88CF6ACB8935802A2B97
    print(h3)  # 177547C0D57AC74748561D054A9CEC14B4C4EA23D1BEFD6F2E69E3A388F90C68

Código en JavaScript (Node.js y navegador)

Usa Web Crypto, disponible en Node.js 18+ y en los navegadores modernos, sin dependencias:

// Node.js 18+ y navegadores modernos (Web Crypto)
async function sha256Hex(texto) {
  const bytes = new TextEncoder().encode(texto); // UTF-8
  const digest = await crypto.subtle.digest('SHA-256', bytes);
  return [...new Uint8Array(digest)].map((b) => b.toString(16).padStart(2, '0')).join('').toUpperCase();
}

const cadena = (campos) => campos.map(([k, v]) => `${k}=${v.trim()}`).join('&');

export function huellaAlta(r, huellaAnterior = '') {
  return sha256Hex(cadena([
    ['IDEmisorFactura', r.emisor],
    ['NumSerieFactura', r.numSerie],
    ['FechaExpedicionFactura', r.fecha],
    ['TipoFactura', r.tipo],
    ['CuotaTotal', r.cuota],
    ['ImporteTotal', r.importe],
    ['Huella', huellaAnterior],
    ['FechaHoraHusoGenRegistro', r.fechaHora],
  ]));
}

export function huellaAnulacion(r, huellaAnterior) {
  return sha256Hex(cadena([
    ['IDEmisorFacturaAnulada', r.emisor],
    ['NumSerieFacturaAnulada', r.numSerie],
    ['FechaExpedicionFacturaAnulada', r.fecha],
    ['Huella', huellaAnterior],
    ['FechaHoraHusoGenRegistro', r.fechaHora],
  ]));
}

// Ejemplos oficiales de la especificación de la AEAT (v0.1.2)
const h1 = await huellaAlta({ emisor: '89890001K', numSerie: '12345678/G33', fecha: '01-01-2024', tipo: 'F1', cuota: '12.35', importe: '123.45', fechaHora: '2024-01-01T19:20:30+01:00' });
const h2 = await huellaAlta({ emisor: '89890001K', numSerie: '12345679/G34', fecha: '01-01-2024', tipo: 'F1', cuota: '12.35', importe: '123.45', fechaHora: '2024-01-01T19:20:35+01:00' }, h1);
const h3 = await huellaAnulacion({ emisor: '89890001K', numSerie: '12345679/G34', fecha: '01-01-2024', fechaHora: '2024-01-01T19:20:40+01:00' }, h2);
console.log(h1, h2, h3);

Estos dos fragmentos se ejecutan en los tests de esta web en cada cambio y deben producir exactamente las huellas oficiales. No publicamos versiones en Java o PHP porque no las hemos podido ejecutar en nuestro entorno de pruebas.

Verificar una cadena completa

Para cada registro, recalcula su huella con sus datos y la huella anterior que declara, y compárala con la que lleva. Después comprueba que la huella anterior que declara (en Encadenamiento/RegistroAnterior) es la del registro previo, y que el emisor, el número y la fecha que declara corresponden a esa factura. Si lo primero falla, se han alterado datos; si falla lo segundo, falta, sobra o se ha reordenado algún registro. Es exactamente lo que hace el comprobador de huellas.

Preguntas frecuentes

¿Qué algoritmo usa la huella de Verifactu?

SHA-256. En el XML se indica con TipoHuella = «01». Según la especificación, es el único algoritmo permitido a la fecha de publicación.

¿La huella va en mayúsculas o minúsculas?

En hexadecimal y en mayúsculas, con 64 caracteres.

¿Qué pasa si la huella no coincide con la que calcula la AEAT?

Según la especificación de la huella, cuando en una remisión de un sistema VERI*FACTU la huella informada no coincide con el cálculo de la AEAT, el registro se marca como «Aceptado con errores». Conviene detectarlo antes con el comprobador de huellas.

¿Se incluye la huella anterior en un registro de anulación?

Sí. Un registro de anulación también se encadena: su cadena incluye la huella del registro anterior de la cadena, sea de alta o de anulación.

Fuentes oficiales

Revisado el 25 de septiembre de 2026
  1. AEAT: Especificaciones técnicas de la huella o hash de los registros (v0.1.2, 27/08/2024)AEAT
  2. AEAT: Descripción de los servicios web VERI*FACTU (v1.0.3, 28/07/2025)AEAT
  3. AEAT: esquemas XSD (SuministroLR, SuministroInformacion, EventosSIF)AEAT
  4. Orden HAC/1177/2024, de 17 de octubre (especificaciones técnicas)BOE

Cada dato de esta página se ha contrastado con estas fuentes el . Si la norma cambia, la actualizamos y cambia esta fecha.