Saltar al contenido
dte-sdk

signDte()

Firma un documento timbrado con tu certificado digital usando XMLDSig, y produce el XML que envías al SII.

Uso básico#

TypeScript
import { readFile } from 'node:fs/promises'
import { loadCertificate, signDte } from 'dte-sdk'
 
const certificate = await loadCertificate(
  await readFile(process.env.DTE_CERT_PATH!),
  process.env.DTE_CERT_PASSWORD!,
)
 
const signed = await signDte(stamped, certificate)
 
signed.signedAt // '2026-10-28T10:15:42-03:00'
signed.xml      // '<DTE version="1.0"><Documento ID="F1024T33">…</Documento><Signature>…</Signature></DTE>'

Firma de tipo#

TypeScript
function signDte(stamped: StampedDte, certificate: Certificate): Promise<SignedDte>
 
function loadCertificate(pfx: Uint8Array, password: string): Promise<Certificate>
 
// SignedDte = StampedDte + { xml: string; signedAt: string }

Parámetros#

  • stamped (StampedDte) - Un documento timbrado con stampDte(). Pasar un Dte sin timbrar es un error de tipos: el SII no acepta documentos sin TED.
  • certificate (Certificate) - El certificado digital cargado con loadCertificate(). Cárgalo una vez y reutilízalo para todos los documentos.

Valor de retorno#

Retorna una Promise<SignedDte>: el documento timbrado más dos campos.

  • xml (string) - El elemento <DTE> completo y firmado, con la <Signature> XMLDSig sobre el <Documento>.
  • signedAt (string) - Cuándo se firmó, como marca de tiempo ISO 8601 con el desfase horario de Chile.

La firma usa Web Crypto, así que funciona igual en Node, Deno, Bun y el navegador. Por eso es asíncrona.

loadCertificate()#

Carga un certificado digital .pfx o .p12. El resultado es un Certificate opaco que pasas a signDte(), sendToSii() y getStatus().

  • pfx (Uint8Array) - Los bytes del archivo del certificado.
  • password (string) - La contraseña que lo protege.

Lanza un DteError con código CERTIFICATE_INVALID cuando el archivo no se puede leer como certificado o la contraseña no lo abre. La contraseña nunca se registra en logs, no se guarda en el resultado y no aparece en los mensajes de error.

Todos los runtimes pueden producir un Uint8Array:

TypeScript
import { readFile } from 'node:fs/promises'
import { loadCertificate } from 'dte-sdk'
 
// A Node Buffer is a Uint8Array
const certificate = await loadCertificate(await readFile('cert.pfx'), password)

Errores#

CódigoCuándo
CERTIFICATE_INVALIDloadCertificate() no puede leer el archivo, o la contraseña es incorrecta

Ejemplos#

Cargar una vez, firmar muchas#

Leer el certificado es la parte costosa. Guarda la promesa en caché y compártela:

TypeScript
import { readFile } from 'node:fs/promises'
import { loadCertificate, signDte, type Certificate, type StampedDte } from 'dte-sdk'
 
let certificate: Promise<Certificate> | undefined
 
function getCertificate(): Promise<Certificate> {
  certificate ??= readFile(process.env.DTE_CERT_PATH!).then((pfx) =>
    loadCertificate(pfx, process.env.DTE_CERT_PASSWORD!),
  )
  return certificate
}
 
export async function sign(stamped: StampedDte) {
  return signDte(stamped, await getCertificate())
}

Firmar varios documentos#

TypeScript
import { signDte } from 'dte-sdk'
 
const signed = await Promise.all(stampedDocs.map((doc) => signDte(doc, certificate)))

Manejar un certificado inválido#

TypeScript
import { DteError, loadCertificate } from 'dte-sdk'
 
try {
  await loadCertificate(pfx, password)
} catch (error) {
  if (error instanceof DteError && error.code === 'CERTIFICATE_INVALID') {
    // The message is generic: it never contains the password
    console.error('Could not open the certificate. Check the file and password.')
  }
  throw error
}

Protege el certificado#

El certificado identifica a una persona real ante el SII. Guarda el archivo y su contraseña en un gestor de secretos, nunca en tu repositorio ni en el bundle del cliente, y limita quién puede leerlos.

Funciones relacionadas#

  • stampDte() - Produce el StampedDte que firmas
  • sendToSii() - Siguiente paso: subir los documentos firmados
  • toXml() - Serializa el documento firmado