Saltar al contenido
dte-sdk

Características

dte-sdk cubre la vida completa de un documento tributario electrónico (DTE) chileno: construirlo, totalizarlo, timbrarlo con un CAF, firmarlo con tu certificado, agruparlo en un sobre, enviarlo al SII y seguir su estado. Alrededor de la emisión cubre también la verificación de los documentos que te envían tus proveedores, los libros del SII, el portal (folios, el registro de compras y ventas), el proceso de certificación y las muestras impresas. Esta página es el mapa: las páginas de la sección, cómo encajan las piezas de la emisión y qué tienen en común todas las funciones del SDK.

Todo lo documentado aquí sale en la 0.8.0, la versión más reciente en npm. La 1.0.0 será la primera versión recomendada para producción: hasta entonces la API puede cambiar en cualquier versión. La mayoría de las áreas se ha probado solo contra respuestas sintéticas del SII. Estado dice qué ha pasado por el SII real y qué no.

Las páginas#

Cómo se componen las piezas de la emisión#

Emitir un documento es una cadena corta. Cada paso recibe la salida del anterior y devuelve un objeto nuevo, así que puedes detenerte en cualquier punto, mirar el resultado y seguir.

1

Crear

createDocument valida la entrada, normaliza RUT y textos, numera las líneas y calcula los totales (mira Totales). Devuelve un Document.

2

Timbrar

stampDocument revisa el documento contra el CAF (tipo, emisor, rango de folios, vencimiento), arma el timbre electrónico (TED) y lo firma con la llave del CAF. Devuelve un StampedDocument.

3

Firmar

signDocument firma el documento con el certificado del emisor (XMLDSig envuelta). Devuelve un SignedDocument con su xml final.

4

Sobre

createEnvelope agrupa documentos firmados bajo una carátula y signEnvelope firma el set con el certificado de quien envía. El resultado es el XML que va al SII.

5

Envío

uploadEnvelope lo envía y waitForUploadStatus sigue la respuesta (servicios web del SII).

En código, paso a paso:

TypeScript
import { readFileSync } from 'node:fs'
import {
  createDocument,
  createEnvelope,
  createSiiClient,
  loadCertificateFromPkcs12,
  parseCaf,
  signDocument,
  signEnvelope,
  stampDocument,
  uploadEnvelope,
  waitForUploadStatus,
} from 'dte-sdk'
 
const certificate = loadCertificateFromPkcs12(readFileSync('firma.pfx'), process.env.PFX_PASSWORD!)
const caf = parseCaf(readFileSync('caf-33.xml', 'utf8'))
 
const document = createDocument(input) // Document: validado, con los totales calculados
const stamped = stampDocument(document, caf) // StampedDocument: agrega el TED
const signed = signDocument(stamped, certificate) // SignedDocument: signed.xml
 
const envelope = signEnvelope(
  createEnvelope({
    kind: 'dte',
    documents: [signed],
    cover: {
      issuerRut: '76543210-3',
      senderRut: certificate.rut!,
      resolutionDate: '2026-10-31',
      resolutionNumber: 0,
    },
  }),
  certificate,
)
 
const sii = createSiiClient({ environment: 'certification', certificate, companyRut: '76543210-3' })
const receipt = await uploadEnvelope(sii, { envelope })
const status = await waitForUploadStatus(sii, { trackId: receipt.trackId })

Cuando no necesitas detenerte a la mitad, issueDocument ejecuta los tres primeros pasos en una sola llamada:

TypeScript
import { issueDocument } from 'dte-sdk'
 
const signed = issueDocument({ document: input, caf, certificate })

Dos helpers sirven en cualquier punto de la cadena: calculateTotals para totalizar un carrito antes de que exista un documento, y buildDocumentXml o toSiiDocument (Documentos) para mirar lo que se enviará. Las boletas (39, 41) van en su propio tipo de sobre, 'receipt' (EnvioBOLETA); mira Sobres.

El XML del CAF contiene una llave privada y el certificado contiene la tuya. Ninguno de los dos va en un repositorio ni en un log. Mira Seguridad.

Qué tienen en común todas las funciones#

Estas reglas valen en todo el SDK, así que las aprendes una sola vez.

  • Un modelo tipado en inglés, documentado en dos idiomas. Los campos están en inglés legible (issuer.businessName, totalAmount), y un codec los convierte a las etiquetas oficiales del SII (RznSoc, MntTotal) en orden de esquema. Cada tipo, función y campo trae JSDoc en inglés y español, con la etiqueta del SII que representa, así tu editor muestra ambos.
  • Datos planos y funciones libres. No hay clases que instanciar. Un Certificate, un Caf, un Document o un cliente del SII es un objeto plano, y cada operación es una función que lo recibe como argumento (signDocument(stamped, certificate), uploadEnvelope(sii, …)).
  • Resultados inmutables. Una función nunca modifica lo que le pasas; devuelve un objeto nuevo. stampDocument devuelve tu documento más un ted, y el original queda como estaba. Los tipos son readonly.
  • El reloj es inyectable. Todo lo que depende de la hora (la fecha de emisión, TSTED, TmstFirma, los chequeos de vencimiento del certificado y del CAF) acepta { now: Date }, así los tests son deterministas. Sin él, el SDK usa la hora de Chile (America/Santiago), nunca la zona horaria del proceso.
  • Una sola clase de error con códigos estables. Todo lanza DteError. Discrimina por error.code, nunca por el mensaje. Los mensajes son genéricos y no llevan RUT, nombres, montos ni llaves; el valor ofensivo, si lo hay, vive en error.details. isDteError(value) lo reconoce y errorCode(value) entrega su código. Mira Errores.
  • Identidad con rut.ts. Cada RUT se valida y normaliza con rut.ts a la forma que usan los esquemas del SII (12345678-5).
  • Las respuestas de negocio son datos. El estado de un envío y el resultado de un reclamo vuelven clasificados (accepted, rejected, un código de reclamo) para que actúes sobre ellos; los errores son para lo que falló en el transporte o en tu entrada.
TypeScript
import { createDocument, isDteError } from 'dte-sdk'
 
try {
  createDocument({ ...input, items: [{ name: 'x'.repeat(81), unitPrice: 1 }] })
} catch (error) {
  if (isDteError(error)) {
    error.code // 'FIELD_TOO_LONG'
    error.details // { field: 'NmbItem', maxLength: 80, length: 81, value: '…' }
  }
}

Dónde vive cada área#

El paquete tiene cuatro puntos de entrada. La raíz corre en cualquier parte; los otros necesitan Node.

ÁreaSe importa desdeCorre en
Documentos, Totales, Timbre, Firma, Sobresdte-sdkNode, Bun y Deno; se empaqueta para el navegador sin configuración
Servicios web del SII (el cliente sobre fetch) y Librosdte-sdkLo mismo
Errores: DteError, isDteErrordte-sdkLo mismo. Todas las entradas lanzan esta clase.
Adaptadores de Node: transporte HTTPS con certificados de cliente, captura HTTP, archivo de envíos, registro de folios en discodte-sdk/nodeNode
Portal, Registro y Certificacióndte-sdk/portalNode (el portal necesita un certificado de cliente, que fetch no puede presentar)
Muestras impresasdte-sdk/pdfNode, con los paquetes opcionales pdf-lib y bwip-js instalados

La entrada raíz nunca menciona un módulo node:: es la parte que puedes empaquetar para un navegador o correr en un runtime de borde. El paquete publica solo módulos ES (un proyecto CommonJS igual puede usar require()) y necesita Node 22.12 o superior. Mira Puntos de entrada para los detalles e Instalación para instalarlo y fijar la versión.

Siguientes pasos#