Inicio rápido
Esta guía emite una factura electrónica tipo 33 de punta a punta: crear el documento, timbrarlo con un CAF, firmarlo con tu certificado, enviarlo al ambiente de certificación del SII y revisar el resultado.
dte-sdk@0.0.2 es una versión placeholder que solo exporta hello().
Esta guía muestra cómo funcionará la API planificada para la 1.0; los
detalles pueden cambiar antes del lanzamiento.
Vas a necesitar un CAF para el tipo 33 y un certificado digital. ¿No conoces esos términos? Lee primero Conceptos clave. ¿Aún no lo instalas? Revisa Instalación.
Pruébalo primero#
No necesitas CAF ni certificado para ver cómo se arma un documento. Edita las líneas de abajo y mira cómo se actualizan los totales y el XML:
Pruébalo — arma un documento
Vista previa de toXml() — sin firma ni TED
<DTE version="1.0">
<Documento ID="F1024T33">
<Encabezado>
<IdDoc>
<TipoDTE>33</TipoDTE>
<Folio>1024</Folio>
<FchEmis>2026-10-30</FchEmis>
</IdDoc>
<Emisor>
<RUTEmisor>76123456-0</RUTEmisor>
<RznSoc>Arrow Software SpA</RznSoc>
</Emisor>
<Receptor>
<RUTRecep>77654321-7</RUTRecep>
</Receptor>
<Totales>
<MntNeto>120000</MntNeto>
<TasaIVA>19</TasaIVA>
<IVA>22800</IVA>
<MntTotal>142800</MntTotal>
</Totales>
</Encabezado>
<Detalle>
<NroLinDet>1</NroLinDet>
<NmbItem>Licencia anual</NmbItem>
<QtyItem>1</QtyItem>
<PrcItem>120000</PrcItem>
<MontoItem>120000</MontoItem>
</Detalle>
</Documento>
</DTE>Los cinco pasos#
Crea el documento
createDte() valida la entrada (dígitos verificadores de los RUT, montos,
campos obligatorios) y calcula los totales. Lanza un DteError en vez de
devolver un documento que el SII rechazaría.
import { createDte } from 'dte-sdk'
const invoice = createDte({
type: 33,
folio: 1024,
issuer: {
rut: '76.123.456-0',
businessName: 'Arrow Software SpA',
activity: 'Desarrollo de software',
acteco: 620200,
address: 'Av. Providencia 1234',
commune: 'Providencia',
},
receiver: {
rut: '77.654.321-7',
businessName: 'Comercial Los Andes Ltda.',
activity: 'Comercio al por menor',
address: 'Huérfanos 1055',
commune: 'Santiago',
},
items: [{ name: 'Licencia anual', quantity: 1, unitPrice: 120000 }],
})
invoice.totals // { net: 120000, exempt: 0, vatRate: 19, vat: 22800, total: 142800 }Tímbralo con el CAF
stampDte() genera el TED, el timbre electrónico, con la llave privada de
tu CAF. El folio 1024 está dentro del rango 1001–2000 del CAF, así que el
timbraje funciona; un folio fuera de él lanza FOLIO_OUT_OF_RANGE.
import { readFile } from 'node:fs/promises'
import { parseCaf, stampDte } from 'dte-sdk'
// The SII delivers CAF files encoded as ISO-8859-1.
const caf = parseCaf(await readFile('./caf/33.xml', 'latin1'))
caf.range // { from: 1001, to: 2000 }
const stamped = stampDte(invoice, caf)
stamped.ted // '<TED version="1.0">…</TED>'
stamped.pdf417 // payload for the barcode on the printed versiondte-sdk no genera PDF: pasa stamped.pdf417 a la librería de códigos
de barra que ya uses para la versión impresa.
Fírmalo con tu certificado
loadCertificate() lee el archivo .pfx y su contraseña una sola vez;
signDte() agrega la firma XMLDSig.
import { readFile } from 'node:fs/promises'
import { loadCertificate, signDte } from 'dte-sdk'
const certificate = await loadCertificate(
await readFile('./certs/signer.pfx'),
process.env.DTE_CERT_PASSWORD!,
)
const signed = await signDte(stamped, certificate)
signed.xml // the signed document, ready to send
signed.signedAt // ISO 8601 timestampEnvíalo al SII
sendToSii() envuelve el documento en un EnvioDTE firmado, se
autentica con el certificado y lo sube. senderRut es el RUT del
titular del certificado, no el de la empresa. En el ambiente de
certificación el número de resolución es 0.
import { sendToSii } from 'dte-sdk'
const { trackId } = await sendToSii(signed, {
certificate,
environment: 'certification',
senderRut: '15.234.567-4',
resolution: { number: 0, date: '2026-09-01' },
})
trackId // '0234567890'Revisa el resultado
El SII valida los envíos de forma asíncrona, así que un track id recién
creado suele responder received primero. Vuelve a consultar después de
una breve espera hasta que quede en accepted,
accepted_with_objections o rejected.
import { getStatus } from 'dte-sdk'
const status = await getStatus(trackId, {
certificate,
environment: 'certification',
issuerRut: '76.123.456-0',
})
status.state // 'accepted'
status.documents // [{ type: 33, folio: 1024, state: 'accepted' }]Todo junto#
El mismo flujo en un solo script, con consulta periódica y manejo de errores:
import { readFile } from 'node:fs/promises'
import {
createDte, parseCaf, stampDte, loadCertificate,
signDte, sendToSii, getStatus, DteError,
} from 'dte-sdk'
const environment = 'certification'
const issuerRut = '76.123.456-0'
async function issueInvoice() {
const caf = parseCaf(await readFile('./caf/33.xml', 'latin1'))
const certificate = await loadCertificate(
await readFile('./certs/signer.pfx'),
process.env.DTE_CERT_PASSWORD!,
)
const invoice = createDte({
type: 33,
folio: 1024,
issuer: {
rut: issuerRut,
businessName: 'Arrow Software SpA',
activity: 'Desarrollo de software',
acteco: 620200,
address: 'Av. Providencia 1234',
commune: 'Providencia',
},
receiver: {
rut: '77.654.321-7',
businessName: 'Comercial Los Andes Ltda.',
activity: 'Comercio al por menor',
address: 'Huérfanos 1055',
commune: 'Santiago',
},
items: [{ name: 'Licencia anual', quantity: 1, unitPrice: 120000 }],
})
const signed = await signDte(stampDte(invoice, caf), certificate)
const { trackId } = await sendToSii(signed, {
certificate,
environment,
senderRut: '15.234.567-4',
resolution: { number: 0, date: '2026-09-01' },
})
// The SII answers asynchronously: poll with a growing delay.
for (let attempt = 1; attempt <= 10; attempt++) {
const status = await getStatus(trackId, { certificate, environment, issuerRut })
if (status.state !== 'received') return status
await new Promise((resolve) => setTimeout(resolve, attempt * 5000))
}
throw new Error(`Still processing, check track id ${trackId} later`)
}
try {
const status = await issueInvoice()
console.log(status.state) // 'accepted'
} catch (error) {
if (error instanceof DteError) {
console.error(error.code) // e.g. 'CAF_EXPIRED', never a RUT or an amount
}
throw error
}Regla práctica: mantén el CAF y el certificado en el servidor, pasa
cada tipo de documento nuevo por certification antes de cambiar
environment a 'production', y guarda el track id junto al folio para
poder volver a consultar el estado más tarde.
A dónde ir ahora#
Una factura más completa: varias líneas, descuentos, ítems exentos y condiciones de pago.
Ejemplo de nota de créditoAnula o corrige una factura con una nota tipo 61 y su referencia.
Conceptos claveQué significan de verdad un CAF, un TED y los estados del SII.
Referencia de la APICada firma, tipo y código de error.