Saltar al contenido
dte-sdk

Referencia de la API

La superficie pública completa de la API planificada para la 1.0: nueve funciones, una clase de error, dos constantes y los tipos que comparten. Todo es un export nombrado de dte-sdk.

TypeScript
import {
  createDte, calculateTotals, stampDte, signDte, loadCertificate,
  sendToSii, getStatus, toXml, parseCaf,
  DteError, DTE_TYPES, VAT_RATE,
  type Dte, type DteType, type LineItem, type Totals, type Caf,
  type StampedDte, type SignedDte, type Certificate,
} from 'dte-sdk'

Funciones#

FunciónFirmaLanza (código de DteError)
createDte()createDte(input): DteMISSING_FIELD, INVALID_RUT, INVALID_AMOUNT
calculateTotals()calculateTotals(dteOrItems, options?): TotalsINVALID_AMOUNT
parseCaf()parseCaf(xml): CafMISSING_FIELD
stampDte()stampDte(dte, caf, options?): StampedDteFOLIO_OUT_OF_RANGE, CAF_MISMATCH, CAF_EXPIRED
loadCertificate()loadCertificate(pfx, password): Promise<Certificate>CERTIFICATE_INVALID
signDte()signDte(stamped, certificate): Promise<SignedDte>—
sendToSii()sendToSii(docs, options): Promise<{ trackId, receivedAt }>SII_UNAVAILABLE, SII_REJECTED
getStatus()getStatus(trackId, options): Promise<Status>SII_UNAVAILABLE
toXml()toXml(doc, options?): string—

Las funciones siguen el ciclo de vida del documento: createDte() → stampDte() → signDte() → sendToSii() → getStatus(). calculateTotals() y toXml() funcionan en cualquier etapa.

createDte()#

TypeScript
function createDte(input: CreateDteInput): Dte

Crea un documento inmutable y validado, y calcula sus totales con las mismas reglas que calculateTotals().

TypeScript
interface CreateDteInput {
  type: DteType
  folio: number
  issuedAt?: string              // 'YYYY-MM-DD', defaults to today in America/Santiago
  issuer: {
    rut: string
    businessName: string
    activity: string
    acteco: number | number[]    // one or more economic activity codes
    address: string
    commune: string
    city?: string
  }
  receiver: {
    rut: string
    businessName: string
    activity?: string
    address?: string
    commune?: string
    city?: string
    email?: string
  }
  items: LineItem[]              // 1 to 60 lines
  references?: Reference[]       // required for types 56 and 61
  paymentMethod?: 'cash' | 'credit' | 'free'
  dueDate?: string               // 'YYYY-MM-DD'
}
 
interface Reference {
  type: DteType | 'SET'          // 'SET' for the SII's certification test set
  folio: number | string
  date: string                   // 'YYYY-MM-DD'
  code?: 1 | 2 | 3               // 1 = voids document, 2 = fixes text, 3 = fixes amounts
  reason?: string
}

Devuelve un Dte: la entrada con los valores por defecto resueltos más totals, todo de solo lectura en profundidad.

Lanza

  • MISSING_FIELD: falta un campo obligatorio o está vacío, incluidas las references en los tipos 56 y 61.
  • INVALID_RUT: el RUT del emisor o del receptor está mal formado o su dígito verificador Módulo 11 es incorrecto.
  • INVALID_AMOUNT: una cantidad, precio o descuento no es un monto válido (negativo, no finito, o un descuento fuera de 0–100).

calculateTotals()#

TypeScript
function calculateTotals(dte: Dte): Totals
function calculateTotals(
  items: LineItem[],
  options?: { type?: DteType; vatRate?: number; grossPrices?: boolean },
): Totals

Calcula neto, exento, IVA y total de un documento o de una lista de líneas. Todos los montos son CLP enteros, con redondeo half-up.

  • Cada línea vale round(quantity × unitPrice × (1 − discountPercent / 100)).
  • Las líneas con exempt: true suman a exempt; en los tipos 34 y 41 todas las líneas son exentas y vat es 0.
  • El IVA se calcula una sola vez, sobre el neto total: vat = round(net × vatRate / 100).
  • Las boletas (tipo 39) usan grossPrices: true por defecto: los precios incluyen IVA, así que net = round(total / 1.19) y vat = total − net.
  • vatRate toma por defecto VAT_RATE (19).
TypeScript
calculateTotals([{ name: 'Licencia anual', quantity: 1, unitPrice: 120000 }])
// { net: 120000, exempt: 0, vatRate: 19, vat: 22800, total: 142800 }
 
calculateTotals([{ name: 'Soporte mensual', quantity: 1, unitPrice: 11900 }], { type: 39 })
// { net: 10000, exempt: 0, vatRate: 19, vat: 1900, total: 11900 }

Lanza INVALID_AMOUNT cuando una línea tiene una cantidad, precio o descuento inválido.

parseCaf()#

TypeScript
function parseCaf(xml: string): Caf

Lee el XML del CAF (Código de Autorización de Folios) que emite el SII. El SII entrega estos archivos en ISO-8859-1, así que léelos con esa codificación.

TypeScript
const caf = parseCaf(await readFile('./caf/33.xml', 'latin1'))
 
caf.type         // 33
caf.issuerRut    // '76.123.456-0'
caf.range        // { from: 1001, to: 2000 }
caf.authorizedAt // 'YYYY-MM-DD'
caf.expiresAt    // 'YYYY-MM-DD'

La llave privada del CAF queda dentro del objeto para que la use stampDte(); no se expone como un campo público.

Lanza MISSING_FIELD cuando al XML le falta un elemento que todo CAF debe tener.

stampDte()#

TypeScript
function stampDte(
  dte: Dte,
  caf: Caf | string,             // a parsed Caf or the raw CAF XML
  options?: { stampedAt?: Date },
): StampedDte

Genera el TED (Timbre Electrónico DTE) del documento y lo firma con la llave privada del CAF.

Devuelve un StampedDte: el documento más ted (el XML del <TED>) y pdf417 (el contenido del código de barras para la versión impresa). dte-sdk no genera PDF.

Lanza

  • FOLIO_OUT_OF_RANGE: el folio no está dentro de caf.range.
  • CAF_MISMATCH: el CAF es de otro tipo de documento o de otro RUT emisor.
  • CAF_EXPIRED: el CAF ya estaba vencido en stampedAt (por defecto, ahora).

loadCertificate()#

TypeScript
function loadCertificate(pfx: Uint8Array, password: string): Promise<Certificate>

Carga un certificado digital .pfx o .p12. Un Buffer de Node es un Uint8Array, así que el resultado de readFile() sirve tal cual. La contraseña nunca se registra en logs, no se guarda en el resultado ni aparece en los errores.

Lanza CERTIFICATE_INVALID cuando el archivo no es un .pfx/.p12 legible o la contraseña no lo abre.

signDte()#

TypeScript
function signDte(stamped: StampedDte, certificate: Certificate): Promise<SignedDte>

Firma el elemento <Documento> con XMLDSig. La firma corre sobre la Web Crypto API, así que funciona en Node, Deno, Bun y el navegador.

Devuelve un SignedDte: el documento timbrado más xml (el documento firmado) y signedAt (una marca de tiempo ISO 8601).

sendToSii()#

TypeScript
function sendToSii(
  docs: SignedDte | SignedDte[],
  options: {
    certificate: Certificate
    environment: 'certification' | 'production'
    senderRut: string                        // RUT of the certificate owner
    resolution: { number: number; date: string }
    signal?: AbortSignal
  },
): Promise<{ trackId: string; receivedAt: string }>

Envuelve los documentos en un EnvioDTE firmado, se autentica ante el SII (semilla y luego token, guardado en caché por certificado), sube el sobre y devuelve el track id. environment no tiene valor por defecto. En certificación, resolution.number es 0.

TypeScript
const { trackId, receivedAt } = await sendToSii(signed, {
  certificate,
  environment: 'certification',
  senderRut: '15.234.567-4',
  resolution: { number: 0, date: '2026-09-01' },
  signal: AbortSignal.timeout(30_000),
})
// trackId: '0234567890'

Lanza

  • SII_UNAVAILABLE: no se pudo contactar al SII o no respondió a tiempo. Es seguro reintentar.
  • SII_REJECTED: el SII rechazó el envío en sí, por ejemplo por errores de autenticación o del sobre. Es distinto de un documento cuyo estado termina en rejected.

getStatus()#

TypeScript
function getStatus(
  trackId: string,
  options: {
    certificate: Certificate
    environment: 'certification' | 'production'
    issuerRut: string
  },
): Promise<Status>
 
type Status = {
  state: 'received' | 'accepted' | 'accepted_with_objections' | 'rejected'
  documents: {
    type: DteType
    folio: number
    state: 'received' | 'accepted' | 'accepted_with_objections' | 'rejected'
    reason?: string
  }[]
}

Consulta al SII el resultado de un envío. state resume el envío; documents trae una entrada por documento, con un reason cuando un documento tiene reparos o fue rechazado. Un track id recién creado suele responder received mientras el SII sigue procesando.

Lanza SII_UNAVAILABLE cuando no se puede contactar al SII.

toXml()#

TypeScript
function toXml(
  doc: Dte | StampedDte | SignedDte,
  options?: { pretty?: boolean; encoding?: 'ISO-8859-1' | 'UTF-8' },
): string

Serializa un documento en cualquier etapa a XML que sigue el esquema del SII. encoding es 'ISO-8859-1' por defecto, que es lo que espera el SII; 'UTF-8' es útil para previsualizar y almacenar.

TypeScript
toXml(invoice, { pretty: true }) // readable, unsigned XML for previews
toXml(signed)                    // the exact signed XML, ISO-8859-1

No uses pretty en nada que vayas a enviar: en un documento firmado, los espacios en blanco son parte del contenido firmado.

Tipos#

DteType#

TypeScript
type DteType = 33 | 34 | 39 | 41 | 46 | 52 | 56 | 61

Revisa Conceptos clave para saber qué significa cada código.

LineItem#

TypeScript
interface LineItem {
  name: string
  description?: string
  quantity: number
  unitPrice: number
  unit?: string                  // unit of measure, e.g. 'UN' or 'KG'
  discountPercent?: number       // 0 to 100
  exempt?: boolean               // keep this line out of the IVA base
}

Totals#

TypeScript
interface Totals {
  net: number      // taxable amount
  exempt: number   // exempt amount
  vatRate: number  // 19
  vat: number      // IVA
  total: number    // net + exempt + vat
}

Dte, StampedDte y SignedDte#

Cada etapa extiende la anterior, así que un SignedDte conserva todos los campos del Dte del que salió. Los tres son de solo lectura en profundidad.

TypeScript
// Simplified: every property is readonly, all the way down.
interface Dte extends CreateDteInput {
  issuedAt: string          // always resolved
  totals: Totals
}
 
interface StampedDte extends Dte {
  ted: string               // the <TED> XML
  pdf417: string            // barcode payload for the printed version
}
 
interface SignedDte extends StampedDte {
  xml: string               // the signed document
  signedAt: string          // ISO 8601 timestamp
}

Caf#

TypeScript
interface Caf {
  readonly type: DteType
  readonly issuerRut: string
  readonly range: { readonly from: number; readonly to: number }
  readonly authorizedAt: string  // 'YYYY-MM-DD'
  readonly expiresAt: string     // 'YYYY-MM-DD'
}

Certificate#

Un identificador opaco que crea loadCertificate() y que aceptan signDte(), sendToSii() y getStatus(). No tiene campos públicos: la llave privada y la contraseña nunca salen de él.

Formas que no se exportan#

CreateDteInput, Reference, Status y la unión de códigos de error se nombran en esta página para facilitar la lectura, pero no se exportan. Derívalas de la API cuando las necesites:

TypeScript
import type { createDte, getStatus, DteError } from 'dte-sdk'
 
type CreateDteInput = Parameters<typeof createDte>[0]
type Reference = NonNullable<CreateDteInput['references']>[number]
type Status = Awaited<ReturnType<typeof getStatus>>
type DteErrorCode = DteError['code']

Constantes#

TypeScript
import { DTE_TYPES, VAT_RATE } from 'dte-sdk'
 
VAT_RATE      // 19
DTE_TYPES[33] // 'Factura electrónica'
DTE_TYPES[61] // 'Nota de crédito electrónica'
ConstanteTipoValor
VAT_RATEnumber19, la tasa de IVA en porcentaje
DTE_TYPESRecord<DteType, string>El nombre oficial de cada tipo soportado

Errores#

Todas las funciones fallan con una sola clase de error:

TypeScript
class DteError extends Error {
  readonly code:
    | 'MISSING_FIELD' | 'INVALID_RUT' | 'INVALID_AMOUNT'
    | 'FOLIO_OUT_OF_RANGE' | 'CAF_MISMATCH' | 'CAF_EXPIRED'
    | 'CERTIFICATE_INVALID' | 'SII_UNAVAILABLE' | 'SII_REJECTED'
}

Los mensajes son genéricos a propósito: nunca incluyen RUT, montos, llaves privadas del CAF ni contraseñas del certificado. Decide según code, no según el texto del mensaje.

CódigoLo lanzaSignificado
MISSING_FIELDcreateDte(), parseCaf()Falta un campo o elemento obligatorio, o está vacío
INVALID_RUTcreateDte()Un RUT está mal formado o tiene el dígito verificador incorrecto
INVALID_AMOUNTcreateDte(), calculateTotals()Una cantidad, precio o descuento no es un monto válido
FOLIO_OUT_OF_RANGEstampDte()El folio está fuera del rango del CAF
CAF_MISMATCHstampDte()El CAF es de otro tipo de documento o de otro RUT emisor
CAF_EXPIREDstampDte()El CAF estaba vencido en la fecha de timbraje
CERTIFICATE_INVALIDloadCertificate()El archivo no es un .pfx/.p12 legible, o la contraseña es incorrecta
SII_UNAVAILABLEsendToSii(), getStatus()No se pudo contactar al SII o se agotó el tiempo; es seguro reintentar
SII_REJECTEDsendToSii()El SII rechazó el envío
TypeScript
import { stampDte, DteError } from 'dte-sdk'
 
try {
  stampDte(invoice, caf)
} catch (error) {
  if (error instanceof DteError && error.code === 'CAF_EXPIRED') {
    // Request a new CAF for this document type, then stamp again.
  }
  throw error
}

Relacionado#